spec_tiller 1.1.3 → 1.2.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
  SHA1:
3
- metadata.gz: 2e4d10c226695ce9372ea1e73d7922b7840cd5c7
4
- data.tar.gz: 99de02747309e0a2702d7065c4738a90151fe6f3
3
+ metadata.gz: d9860619a722afce5c9d9fd35042c134bad7e4a3
4
+ data.tar.gz: 7409ba1e7a22b25c7f1b51b6122cf7349412b998
5
5
  SHA512:
6
- metadata.gz: 5365c4ec86b284ab4b826fab9439646b54683038d4cc5718dc787beb7c048fcaf27757f74173aff0e71728e7f25f225b09ee40c34ea49e8daa2003477dce7d57
7
- data.tar.gz: 4b4b6f852f476291a52eb01ccaf042d090bc0e6ee8ec053c746918b0d0331623226e24835e73e90cd5875ff30885e87c22789ce799977dae80792e20cf71db02
6
+ metadata.gz: 22d2ed32885a2e3a48c160e8a4fbecc7d2ede996a9ba51d946d57f5422e91e7328e94bf2e8ae0883927b5179730e79f6952e435f40eb587c2bb541a9c6825df8
7
+ data.tar.gz: 7a36f2b751515ce1734e6ede356758e983cfd94e98278d6d0eaecd66c694499fb0ad7393687537880ab8c73d1d66151c6278352f4d4663fda895000c0da54912
data/.travis.yml ADDED
@@ -0,0 +1,22 @@
1
+ ---
2
+ language: ruby
3
+ rvm:
4
+ - 2.1.2
5
+ branches:
6
+ only:
7
+ - develop
8
+ - master
9
+ - "/^release\\/.*$/"
10
+ - "/^hotfix\\/.*$/"
11
+ - "/^feature\\/.*$/"
12
+ cache: bundler
13
+ script:
14
+ - RSPEC_RETRY_COUNT=2 bundle exec rspec $TEST_SUITE --tag ~local_only
15
+ - if [[ $RUN_JS == "true" ]]; then bundle exec rake jasmine:ci; fi
16
+ env:
17
+ global:
18
+ - IGNORE_SPECS="spec/documents/*"
19
+ matrix:
20
+ - TEST_SUITE="spec/tasks/spec_tiller_rake_spec.rb"
21
+ - TEST_SUITE="spec/build_matrix_parser_spec.rb spec/distribute_spec_files_spec.rb spec/sync_spec_file_list_spec.rb"
22
+ num_builds: 2
data/README.md CHANGED
@@ -17,7 +17,10 @@ And then execute:
17
17
 
18
18
  ## Set Up
19
19
 
20
- #### Git Hooks
20
+ #### Git Hooks
21
+
22
+ ###### Sync
23
+
21
24
  In order to make sure that the travis file remains up to date with any newly added or removed spec files, you'll have to include a rake task in your pre-commit and post-merge git hooks. Create two files in .git/hooks/ --> ``pre-commit`` and ``post-merge`` (no file extension). **Please make sure that these files are executable** (more information on [git hooks](http://git-scm.com/book/en/Customizing-Git-Git-Hooks)). In each of those two files, add the following rake task:
22
25
 
23
26
  #!/bin/sh
@@ -29,6 +32,17 @@ Upon setting this up, any time you commit or merge, you'll notice the following
29
32
  Removed: [spec/file/removed_spec.rb]
30
33
  Added: [spec/file/added_spec.rb, spec/file/another_added_spec.rb]
31
34
 
35
+ ###### Redistribute
36
+
37
+ You can also set up a hook to redistribute spec files with each commit. Because this task looks at previous travis builds for a specified branch, it's best to either add it to your post merge hook or point it to a branch that you know has builds on travis. Follow the instructions above and create a file that looks like this:
38
+
39
+ #!/bin/sh
40
+ rake spec_tiller:redistribute [ BRANCH='branch_name' ]
41
+
42
+ If you don't specify a branch_name, 'develop' will be used. You shouldn't use the 'local' option (see below) in a git hook - it may take a long time to run.
43
+
44
+ The sync task is called from the redistribute task, so you shouldn't need both hooks.
45
+
32
46
  ####.travis.yml
33
47
  In your ``.travis.yml`` file, create a top-level variable **num_builds** and set it to the number of builds you want the spec files distributed over (default value is 5 builds). You must also make sure that the base of your script is as follows:
34
48
 
@@ -72,11 +86,77 @@ Here is an example of what a ``.travis.yml`` file may look like after all is sai
72
86
  num_builds: 5
73
87
 
74
88
  #### Redistributing Files
75
- Initially, and every so often, you will have to redistribute the spec files (make sure everything is properly set up before running this rake task). The *spec_tiller:sync* rake task adds any new spec files to the last bucket, so after a little while, the timing of the build may not be optimal. In order to redistribute the spec files in order to optimize test suite run time, run the following rake task:
89
+ In order to keep your jobs relatively even in length (*spec_tiller:sync* rake task adds new files to a random job), you should run redistribute occasionally. Redistribute uses the rspec profile results to distribute specs into jobs that will run in roughly the same amount of time. You can call the task as specified below:
90
+
91
+ rake spec_tiller:redistribute [ BRANCH='branch_name']
92
+
93
+ where the 'branch_name argument is optional. There are two ways to set up the redistribute task.
94
+
95
+ ######Local Redistribute
96
+ This option is the most simple - it doesn't require any extra setup. The task will run your whole test suite locally and use the profile results to redistribute your files. However, because it needs to be run locally, it works best for projects with a relatively small test suite. Larger projects should use the travis setup. To run 'local' redistribute, specify 'local' as the branch name:
97
+
98
+ rake spec_tiller:redistribute BRANCH='local'
99
+
100
+ ######Travis Redistribute
101
+ This option uses profile results from past travis builds to redistribute your test suite. After it is finished running, the sync task is invoked to ensure any new files were also added. There are a few extra pieces of setup required:
102
+
103
+ * Add the --profile 1000000000 rspec option to your travis script. This will cause travis to print all profile results in the build log. It should look something like this:
104
+
105
+ bundle exec rspec $TEST_SUITE --tag ~local_only --profile 1000000000
106
+
107
+ * Create a github auth key for travis and add it to your ~./bash_profile. The key should be called GITHUB_TOKEN_FOR_TRAVIS_API and should have a value of the key. You can create a key on this page: [https://github.com/settings/applications](https://github.com/settings/applications). Generate a new personal access token for travis with the following scopes:
108
+
109
+ repo, public_repo, repo:status, read:org, read:public_key
110
+
111
+ * Optionally, you can set up your travis script so that profile results can be folded. This will prevent your build log from getting cluttered. One approach is to store profile results in another file, then call it in an after script. There are two ways to do this, but either way you'll need to include this after script in your travis file:
112
+
113
+ ``.travis.yml``:
114
+
115
+ after_script:
116
+ - cat /tmp/profile_results.txt
117
+
118
+ * The best way is to use a custom rspec formatter. An example exists in this project at spec/support/formatters/profile_to_file_formatter.rb. You can tell rspec to use the formatter in your spec_helper or rails_helper:
119
+
120
+ ``spec_helper.rb``:
121
+
122
+ RSpec.configure do |config|
123
+
124
+ if ENV['CI_ENV'] == 'travis'
125
+ config.add_formatter('progress')
126
+ config.add_formatter(ProfileToFileFormatter)
127
+ end
128
+ end
129
+
130
+ * The other option is to read stdout and watch for the profile results. You'll most likely want to move your script into a separate file, then update your travis script to run from that file. See examples below:
131
+
132
+ ``.travis.yml``:
133
+
134
+ script: ./scripts/travis_script.sh
135
+
136
+ ``scripts/travis_script.sh``:
137
+
138
+ #!/bin/bash
139
+
140
+ set -v
141
+
142
+ bundle exec rspec $TEST_SUITE --tag ~local_only --profile 1000000000 --tty | while read line
143
+ do
144
+ if [[ $line =~ Top.[0-9]+.slowest.examples ]]; then
145
+ INPROF=true
146
+ echo "$line" > /tmp/profile_results.txt
147
+ elif [[ $line =~ Coverage.report.generated ]]; then
148
+ INPROF=false
149
+ elif [[ $INPROF == true ]]; then
150
+ echo "$line" >> /tmp/profile_results.txt
151
+ else
152
+ echo -e "$line"
153
+ fi
154
+ done
155
+
76
156
 
77
- spec_tiller:redistribute
157
+ To call travis redistribute, specify the branch that should be used as the base. The most recent build for the branch must have the '--profile 1000000000' option specified. If no branch is specified, 'develop' will be used. Below is an example call:
78
158
 
79
- This will run your whole test suite, keeping track of how long each spec file takes to run, and the will distribute the spec files in order to maximize your test suite's run time, over the number of builds you've designated (with a default value of 5). **This rake task will not print the output of the rake task until it is complete.**
159
+ rake spec_tiller:redistribute BRANCH='develop'
80
160
 
81
161
  #### Ignoring Files
82
162
  By default, both sync and redistribute will look for any tests following the "spec/**/*_spec.rb" pattern. If you want the tasks to ignore any specs, you can add the IGNORE_SPECS variable to your global variables. The value should be the patterns or specs you want to exclude, separated by spaces.
@@ -26,14 +26,29 @@ module SyncSpecFiles
26
26
 
27
27
  module_function :rewrite_travis_content
28
28
 
29
+ def self.sync
30
+ content = YAML::load(File.open('.travis.yml'))
31
+ current_file_list = Dir.glob('spec/**/*_spec.rb').map { |file_path| file_path.slice(/(spec\/\S+$)/) }
32
+
33
+ puts "\nSyncing list of spec files..."
34
+
35
+ SyncSpecFiles.rewrite_travis_content(content, current_file_list) do |content, original, current_file_list|
36
+ File.open('.travis.yml', 'w') { |file| file.write(content.to_yaml(:line_width => -1)) }
37
+ puts SyncSpecFiles.file_diff(original, current_file_list)
38
+ end
39
+
40
+ `git add .travis.yml`
41
+ end
42
+
29
43
  def self.get_ignored_specs(content)
30
44
  ignore_specs = []
31
45
  content['env']['global'].each do |row|
32
- next unless row.is_a?(String)
33
- # Input: IGNORE_SPECS="spec/a.rb spec/b.rb"
34
- # Output: ['spec/a.rb spec/b.rb']
35
- matches = row.match(/IGNORE_SPECS="\s*([^"]+)"/)
36
- ignore_specs << matches[1].split(' ') unless matches.nil?
46
+ if row.is_a?(String)
47
+ # Input: IGNORE_SPECS="spec/a.rb spec/b.rb"
48
+ # Output: ['spec/a.rb spec/b.rb']
49
+ matches = row.match(/IGNORE_SPECS="\s*([^"]+)"/)
50
+ ignore_specs << matches[1].split(' ') unless matches.nil?
51
+ end
37
52
  end
38
53
  ignore_specs.flatten
39
54
  end
@@ -57,9 +72,11 @@ module SyncSpecFiles
57
72
 
58
73
  def self.add_new_files(original, buckets, current_file_list)
59
74
  buckets_clone = buckets.map(&:dup)
75
+ num_buckets = buckets.length
60
76
 
61
77
  added_files(original, current_file_list).each do |spec_file|
62
- buckets_clone.last << spec_file
78
+ bucket_index = rand(num_buckets)
79
+ buckets_clone[bucket_index] << spec_file
63
80
  end
64
81
 
65
82
  buckets_clone
@@ -0,0 +1,47 @@
1
+ require 'travis'
2
+
3
+ class TravisAPI
4
+
5
+ def self.get_logs(branch)
6
+ client = Travis::Client.new('https://api.travis-ci.com')
7
+ client.github_auth(ENV.fetch('GITHUB_TOKEN_FOR_TRAVIS_API'))
8
+ repository = client.repo(current_repo)
9
+
10
+ raise 'Repository not found. Ensure Fetch URL of "git remote show origin" points to your repository.' if repository.nil?
11
+
12
+ raise "Branch #{branch} not found in current repository." unless repository.branches.key?(branch)
13
+
14
+ last_build = most_recent_build_for(repository, branch)
15
+
16
+ logs_for(last_build)
17
+ end
18
+
19
+
20
+
21
+ def self.current_repo
22
+ # Input:
23
+ # ...
24
+ # Fetch URL: git@github.com:grnhse/spec-tiller.git
25
+ # ...
26
+ # Output: grnhse/spec-tiller
27
+ `git remote show -n origin`.match(/Fetch URL: .*:(.+).git/)[1]
28
+ end
29
+
30
+ def self.most_recent_build_for(repository, branch)
31
+ repository.each_build do |build|
32
+ if build.commit.branch == branch && build.state == 'passed'
33
+ return build
34
+ end
35
+ end
36
+
37
+ raise "No passing builds found for #{branch}."
38
+ end
39
+
40
+ def self.logs_for(build)
41
+ build.
42
+ jobs.
43
+ map{ |j| j.log.body }.
44
+ compact.
45
+ join('\n')
46
+ end
47
+ end
@@ -1,3 +1,3 @@
1
1
  module SpecTiller
2
- VERSION = '1.1.3'
2
+ VERSION = '1.2.0'
3
3
  end
@@ -1,36 +1,37 @@
1
+ require 'spec_tiller/travis_api'
2
+
1
3
  namespace :spec_tiller do
2
4
  desc 'Compares spec files in travis.yml to current list of spec files, and syncs accordingly'
3
5
  task :sync do
4
- content = YAML::load(File.open('.travis.yml'))
5
- current_file_list = Dir.glob('spec/**/*_spec.rb').map { |file_path| file_path.slice(/(spec\/\S+$)/) }
6
-
7
- puts "\nSyncing list of spec files..."
8
-
9
- SyncSpecFiles.rewrite_travis_content(content, current_file_list) do |content, original, current_file_list|
10
- File.open('.travis.yml', 'w') { |file| file.write(content.to_yaml(:line_width => -1)) }
11
- puts SyncSpecFiles.file_diff(original, current_file_list)
12
- end
13
-
14
- `git add .travis.yml`
6
+ SyncSpecFiles.sync
15
7
  end
16
8
 
17
9
  desc 'Runs whole test suite and redistributes spec files across builds according to file run time'
18
10
  task :redistribute => :environment do
11
+
12
+ branch = ENV['BRANCH'] ? ENV['BRANCH'] : 'develop'
13
+
14
+ `echo "#{branch}"`
19
15
  travis_yml_file = YAML::load(File.open('.travis.yml'))
20
- env_variables = travis_yml_file['env']['global']
21
- script = travis_yml_file['script'].first.gsub('$TEST_SUITE ', '')
22
16
 
23
- ignore_specs = SyncSpecFiles.get_ignored_specs(travis_yml_file).map { |spec| %Q("#{spec}") }
24
- script += %Q( --exclude-pattern #{ignore_specs.join(',')}) unless ignore_specs.empty?
17
+ if branch == 'local'
18
+ env_variables = travis_yml_file['env']['global']
19
+ script = travis_yml_file['script'].first.gsub('$TEST_SUITE ', '')
25
20
 
26
- profile_results = `#{env_variables.join(' ')} #{script} --profile 1000000000`
21
+ ignore_specs = SyncSpecFiles.get_ignored_specs(travis_yml_file).map { |spec| %Q("#{spec}") }
22
+ script += %Q( --exclude-pattern #{ignore_specs.join(',')}) unless ignore_specs.empty?
23
+
24
+ profile_results = `#{env_variables.join(' ')} #{script} --profile 1000000000`
25
+ else
26
+ profile_results = TravisAPI.get_logs(branch)
27
+ end
27
28
 
28
- `echo "#{profile_results}" > spec/log/rspec_profile_output.txt`
29
29
  TravisBuildMatrix::SpecDistributor.new(travis_yml_file, profile_results) do |content|
30
30
  File.open('.travis.yml', 'w') { |file| file.write(content.to_yaml(:line_width => -1)) }
31
31
  end
32
32
 
33
- puts profile_results
33
+ SyncSpecFiles.sync
34
+
34
35
  end
35
36
 
36
- end
37
+ end
data/spec/spec_helper.rb CHANGED
@@ -3,6 +3,12 @@ Bundler.setup
3
3
 
4
4
  require 'spec_tiller' # and any other gems you need
5
5
 
6
+ Dir["./spec/support/**/*.rb"].sort.each { |f| require f }
7
+
6
8
  RSpec.configure do |config|
7
- # some (optional) config here
9
+
10
+ if ENV['CI_ENV'] == 'travis'
11
+ config.add_formatter('progress')
12
+ config.add_formatter(ProfileToFileFormatter)
13
+ end
8
14
  end
@@ -0,0 +1,17 @@
1
+ require 'rspec'
2
+ require 'rspec/core/formatters/profile_formatter'
3
+
4
+ class ProfileToFileFormatter < RSpec::Core::Formatters::ProfileFormatter
5
+ RSpec::Core::Formatters.register(self, :dump_profile)
6
+
7
+ def initialize(_)
8
+ @output = File.new('/tmp/profile_results.txt', 'w')
9
+
10
+ end
11
+
12
+ # This class really just changes @output,
13
+ # everything else can function as it does in ProfileFormatter
14
+ def dump_profile(profile)
15
+ super(profile)
16
+ end
17
+ end
@@ -18,19 +18,8 @@ describe 'SyncSpecFiles' do
18
18
  SyncSpecFiles.rewrite_travis_content(travis_yaml, current_file_list)
19
19
  end
20
20
 
21
- it 'adds new files to the last line' do
22
- last_line = travis_yaml['env']['matrix'].last
23
-
24
- expect(last_line).to include('spec/test/new1.rb','spec/test2/new2.rb','spec/test/new3.rb')
25
- end
26
-
27
- it "doesn't add new files to other lines" do
28
- travis_yaml['env']['matrix'][0..-2].each do |bucket|
29
- expect(bucket).not_to include('spec/test/new1.rb')
30
- expect(bucket).not_to include('spec/test2/new2.rb')
31
- expect(bucket).not_to include('spec/test/new3.rb')
32
- end
33
-
21
+ it 'adds new files to random line' do
22
+ expect(travis_yaml['env']['matrix'].join(' ')).to include('spec/test/new1.rb','spec/test2/new2.rb','spec/test/new3.rb')
34
23
  end
35
24
 
36
25
  it 'removes unused specs' do
@@ -40,8 +29,10 @@ describe 'SyncSpecFiles' do
40
29
 
41
30
  end
42
31
 
43
- it 'removes unused buckets' do
44
- expect(travis_yaml['env']['matrix'].length).to eq(2)
32
+ it 'removes lines without a TEST_SUITE' do
33
+ travis_yaml['env']['matrix'].each do |bucket|
34
+ expect(bucket).to include('TEST_SUITE="')
35
+ end
45
36
  end
46
37
 
47
38
  it 'does not include ignored specs' do
@@ -12,14 +12,14 @@ describe 'spec_tiller:sync' do
12
12
  end
13
13
  end
14
14
 
15
- it 'adds new files to last line' do
15
+ it 'adds new files to random line' do
16
16
  temp_file_name = 'spec/documents/new_added_spec.rb'
17
17
  File.new(temp_file_name, 'w')
18
18
  content_at_start = YAML::load(File.open('.travis.yml'))
19
19
 
20
20
  task.invoke
21
21
  content_at_end = YAML::load(File.open('.travis.yml'))
22
- expect(content_at_end['env']['matrix'].last).to include(temp_file_name)
22
+ expect(content_at_end['env']['matrix'].join(' ')).to include(temp_file_name)
23
23
 
24
24
  # Clean up added file and travis.yml
25
25
  File.delete(temp_file_name)
@@ -45,9 +45,13 @@ end
45
45
 
46
46
  describe 'spec_tiller:redistribute' do
47
47
  include_context('rake')
48
+ before do
49
+ allow(SyncSpecFiles).to receive(:sync)
50
+ end
48
51
 
49
52
  it 'distributes evenly based on run time' do
50
53
  content_at_start = YAML::load(File.open('.travis.yml'))
54
+ ENV['BRANCH'] = 'local'
51
55
  task.invoke
52
56
  content_at_end = YAML::load(File.open('.travis.yml'))
53
57
 
data/spec_tiller.gemspec CHANGED
@@ -26,4 +26,5 @@ Gem::Specification.new do |spec|
26
26
  spec.add_development_dependency "rake"
27
27
  spec.add_development_dependency "rspec"
28
28
  spec.add_development_dependency "railties"
29
+ spec.add_dependency "travis", ">= 1.6.0"
29
30
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spec_tiller
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.3
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matt Schmaus
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2015-01-07 00:00:00.000000000 Z
12
+ date: 2015-02-02 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
@@ -67,6 +67,20 @@ dependencies:
67
67
  - - ">="
68
68
  - !ruby/object:Gem::Version
69
69
  version: '0'
70
+ - !ruby/object:Gem::Dependency
71
+ name: travis
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: 1.6.0
77
+ type: :runtime
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: 1.6.0
70
84
  description: |2
71
85
  This gem will parse the output of calling "rspec --perform", then will redistribute
72
86
  the spec files evenly, based on file run time, across all builds established
@@ -79,6 +93,7 @@ extensions: []
79
93
  extra_rdoc_files: []
80
94
  files:
81
95
  - ".gitignore"
96
+ - ".travis.yml"
82
97
  - Gemfile
83
98
  - LICENSE.txt
84
99
  - README.md
@@ -88,6 +103,7 @@ files:
88
103
  - lib/spec_tiller/distribute_spec_files.rb
89
104
  - lib/spec_tiller/railtie.rb
90
105
  - lib/spec_tiller/sync_spec_file_list.rb
106
+ - lib/spec_tiller/travis_api.rb
91
107
  - lib/spec_tiller/version.rb
92
108
  - lib/tasks/spec_tiller.rake
93
109
  - spec/build_matrix_parser_spec.rb
@@ -101,6 +117,7 @@ files:
101
117
  - spec/documents/iterator5_spec.rb
102
118
  - spec/documents/rspec_profile_results.txt
103
119
  - spec/spec_helper.rb
120
+ - spec/support/formatters/profile_to_file_formatter.rb
104
121
  - spec/support/shared_contexts/rake.rb
105
122
  - spec/sync_spec_file_list_spec.rb
106
123
  - spec/tasks/spec_tiller_rake_spec.rb
@@ -141,6 +158,7 @@ test_files:
141
158
  - spec/documents/iterator5_spec.rb
142
159
  - spec/documents/rspec_profile_results.txt
143
160
  - spec/spec_helper.rb
161
+ - spec/support/formatters/profile_to_file_formatter.rb
144
162
  - spec/support/shared_contexts/rake.rb
145
163
  - spec/sync_spec_file_list_spec.rb
146
164
  - spec/tasks/spec_tiller_rake_spec.rb