knapsack_pro 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (100) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -3
  3. data/CHANGELOG.md +4 -0
  4. data/README.md +368 -4
  5. data/bin/knapsack_pro +20 -0
  6. data/circle.yml +2 -0
  7. data/knapsack_pro.gemspec +6 -1
  8. data/lib/knapsack_pro.rb +74 -0
  9. data/lib/knapsack_pro/adapters/base_adapter.rb +30 -0
  10. data/lib/knapsack_pro/adapters/cucumber_adapter.rb +40 -0
  11. data/lib/knapsack_pro/adapters/minitest_adapter.rb +52 -0
  12. data/lib/knapsack_pro/adapters/rspec_adapter.rb +48 -0
  13. data/lib/knapsack_pro/allocator.rb +40 -0
  14. data/lib/knapsack_pro/allocator_builder.rb +40 -0
  15. data/lib/knapsack_pro/client/api/action.rb +17 -0
  16. data/lib/knapsack_pro/client/api/v1/base.rb +15 -0
  17. data/lib/knapsack_pro/client/api/v1/build_distributions.rb +29 -0
  18. data/lib/knapsack_pro/client/api/v1/build_subsets.rb +29 -0
  19. data/lib/knapsack_pro/client/connection.rb +94 -0
  20. data/lib/knapsack_pro/config/ci/base.rb +22 -0
  21. data/lib/knapsack_pro/config/ci/buildkite.rb +27 -0
  22. data/lib/knapsack_pro/config/ci/circle.rb +29 -0
  23. data/lib/knapsack_pro/config/ci/semaphore.rb +28 -0
  24. data/lib/knapsack_pro/config/env.rb +111 -0
  25. data/lib/knapsack_pro/logger_wrapper.rb +15 -0
  26. data/lib/knapsack_pro/presenter.rb +25 -0
  27. data/lib/knapsack_pro/report.rb +20 -0
  28. data/lib/knapsack_pro/repository_adapter_initiator.rb +12 -0
  29. data/lib/knapsack_pro/repository_adapters/base_adapter.rb +14 -0
  30. data/lib/knapsack_pro/repository_adapters/env_adapter.rb +13 -0
  31. data/lib/knapsack_pro/repository_adapters/git_adapter.rb +19 -0
  32. data/lib/knapsack_pro/runners/base_runner.rb +31 -0
  33. data/lib/knapsack_pro/runners/cucumber_runner.rb +16 -0
  34. data/lib/knapsack_pro/runners/minitest_runner.rb +26 -0
  35. data/lib/knapsack_pro/runners/rspec_runner.rb +16 -0
  36. data/lib/knapsack_pro/task_loader.rb +11 -0
  37. data/lib/knapsack_pro/test_file_cleaner.rb +7 -0
  38. data/lib/knapsack_pro/test_file_finder.rb +33 -0
  39. data/lib/knapsack_pro/test_file_pattern.rb +7 -0
  40. data/lib/knapsack_pro/test_file_presenter.rb +11 -0
  41. data/lib/knapsack_pro/test_flat_distributor.rb +84 -0
  42. data/lib/knapsack_pro/tracker.rb +64 -0
  43. data/lib/knapsack_pro/version.rb +1 -1
  44. data/lib/tasks/cucumber.rake +7 -0
  45. data/lib/tasks/minitest.rake +7 -0
  46. data/lib/tasks/rspec.rake +7 -0
  47. data/spec/fixtures/vcr_cassettes/api/v1/build_distributions/subset/invalid_test_suite_token.yml +50 -0
  48. data/spec/fixtures/vcr_cassettes/api/v1/build_distributions/subset/success.yml +52 -0
  49. data/spec/fixtures/vcr_cassettes/api/v1/build_subsets/create/invalid_test_suite_token.yml +50 -0
  50. data/spec/fixtures/vcr_cassettes/api/v1/build_subsets/create/success.yml +50 -0
  51. data/spec/integration/api/build_distributions_subset_spec.rb +74 -0
  52. data/spec/integration/api/build_subsets_create_spec.rb +76 -0
  53. data/spec/knapsack_pro/adapters/base_adapter_spec.rb +63 -0
  54. data/spec/knapsack_pro/adapters/cucumber_adapter_spec.rb +71 -0
  55. data/spec/knapsack_pro/adapters/minitest_adapter_spec.rb +107 -0
  56. data/spec/knapsack_pro/adapters/rspec_adapter_spec.rb +94 -0
  57. data/spec/knapsack_pro/allocator_builder_spec.rb +45 -0
  58. data/spec/knapsack_pro/allocator_spec.rb +80 -0
  59. data/spec/knapsack_pro/client/api/action_spec.rb +17 -0
  60. data/spec/knapsack_pro/client/api/v1/base_spec.rb +2 -0
  61. data/spec/knapsack_pro/client/api/v1/build_distributions_spec.rb +35 -0
  62. data/spec/knapsack_pro/client/api/v1/build_subsets_spec.rb +35 -0
  63. data/spec/knapsack_pro/client/connection_spec.rb +138 -0
  64. data/spec/knapsack_pro/config/ci/base_spec.rb +7 -0
  65. data/spec/knapsack_pro/config/ci/buildkite_spec.rb +74 -0
  66. data/spec/knapsack_pro/config/ci/circle_spec.rb +74 -0
  67. data/spec/knapsack_pro/config/ci/semaphore_spec.rb +74 -0
  68. data/spec/knapsack_pro/config/env_spec.rb +368 -0
  69. data/spec/knapsack_pro/logger_wrapper_spec.rb +15 -0
  70. data/spec/knapsack_pro/presenter_spec.rb +57 -0
  71. data/spec/knapsack_pro/report_spec.rb +68 -0
  72. data/spec/knapsack_pro/repository_adapter_initiator_spec.rb +21 -0
  73. data/spec/knapsack_pro/repository_adapters/base_adapter_spec.rb +13 -0
  74. data/spec/knapsack_pro/repository_adapters/env_adapter_spec.rb +27 -0
  75. data/spec/knapsack_pro/repository_adapters/git_adapter_spec.rb +27 -0
  76. data/spec/knapsack_pro/runners/base_runner_spec.rb +61 -0
  77. data/spec/knapsack_pro/runners/cucumber_runner_spec.rb +47 -0
  78. data/spec/knapsack_pro/runners/minitest_runner_spec.rb +34 -0
  79. data/spec/knapsack_pro/runners/rspec_runner_spec.rb +49 -0
  80. data/spec/knapsack_pro/task_loader_spec.rb +14 -0
  81. data/spec/knapsack_pro/test_file_cleaner_spec.rb +11 -0
  82. data/spec/knapsack_pro/test_file_finder_spec.rb +35 -0
  83. data/spec/knapsack_pro/test_file_pattern_spec.rb +23 -0
  84. data/spec/knapsack_pro/test_file_presenter_spec.rb +22 -0
  85. data/spec/knapsack_pro/test_flat_distributor_spec.rb +60 -0
  86. data/spec/knapsack_pro/tracker_spec.rb +102 -0
  87. data/spec/knapsack_pro_spec.rb +56 -0
  88. data/spec/spec_helper.rb +11 -1
  89. data/spec/support/fakes/cucumber.rb +12 -0
  90. data/spec/support/fakes/minitest.rb +12 -0
  91. data/spec/support/shared_examples/adapter.rb +17 -0
  92. data/spec_fake/controllers/users_controller_spec.rb +0 -0
  93. data/spec_fake/models/admin_spec.rb +0 -0
  94. data/spec_fake/models/user_spec.rb +0 -0
  95. data/spec_fake/spec_helper.rb +0 -0
  96. data/test_fake/a_test.rb +0 -0
  97. data/test_fake/b_test.rb +0 -0
  98. metadata +212 -12
  99. data/Gemfile.lock +0 -69
  100. data/spec/knapsack_spec.rb +0 -3
@@ -0,0 +1,94 @@
1
+ module KnapsackPro
2
+ module Client
3
+ class Connection
4
+ TIMEOUT = 15
5
+
6
+ def initialize(action)
7
+ @action = action
8
+ end
9
+
10
+ def call
11
+ send(action.http_method)
12
+ end
13
+
14
+ def success?
15
+ !!response
16
+ end
17
+
18
+ def errors?
19
+ !!(response && (response['errors'] || response['error']))
20
+ end
21
+
22
+ private
23
+
24
+ attr_reader :action, :response
25
+
26
+ def logger
27
+ KnapsackPro.logger
28
+ end
29
+
30
+ def endpoint
31
+ KnapsackPro::Config::Env.endpoint
32
+ end
33
+
34
+ def endpoint_url
35
+ endpoint + action.endpoint_path
36
+ end
37
+
38
+ def request_hash
39
+ action
40
+ .request_hash
41
+ .merge({
42
+ test_suite_token: test_suite_token
43
+ })
44
+ end
45
+
46
+ def request_body
47
+ request_hash.to_json
48
+ end
49
+
50
+ def test_suite_token
51
+ KnapsackPro::Config::Env.test_suite_token
52
+ end
53
+
54
+ def json_headers
55
+ {
56
+ 'Content-Type' => 'application/json',
57
+ 'Accept' => 'application/json'
58
+ }
59
+ end
60
+
61
+ def parse_response(body)
62
+ return '' if body == '' || body.nil?
63
+ JSON.parse(body)
64
+ rescue JSON::ParserError
65
+ nil
66
+ end
67
+
68
+ def post
69
+ uri = URI.parse(endpoint_url)
70
+ http = Net::HTTP.new(uri.host, uri.port)
71
+ http.use_ssl = (uri.scheme == 'https')
72
+ http.open_timeout = TIMEOUT
73
+ http.read_timeout = TIMEOUT
74
+
75
+ http_response = http.post(uri.path, request_body, json_headers)
76
+ @response = parse_response(http_response.body)
77
+
78
+ request_uuid = http_response.header['X-Request-Id']
79
+
80
+ logger.info("API request UUID: #{request_uuid}")
81
+ logger.info('API response:')
82
+ if errors?
83
+ logger.error(response)
84
+ else
85
+ logger.info(response)
86
+ end
87
+
88
+ response
89
+ rescue Errno::ECONNREFUSED, EOFError, Net::OpenTimeout, Net::ReadTimeout => e
90
+ logger.warn(e.inspect)
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,22 @@
1
+ module KnapsackPro
2
+ module Config
3
+ module CI
4
+ class Base
5
+ def node_total
6
+ end
7
+
8
+ def node_index
9
+ end
10
+
11
+ def commit_hash
12
+ end
13
+
14
+ def branch
15
+ end
16
+
17
+ def project_dir
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,27 @@
1
+ module KnapsackPro
2
+ module Config
3
+ module CI
4
+ class Buildkite < Base
5
+ def node_total
6
+ ENV['BUILDKITE_PARALLEL_JOB_COUNT']
7
+ end
8
+
9
+ def node_index
10
+ ENV['BUILDKITE_PARALLEL_JOB']
11
+ end
12
+
13
+ def commit_hash
14
+ ENV['BUILDKITE_COMMIT']
15
+ end
16
+
17
+ def branch
18
+ ENV['BUILDKITE_BRANCH']
19
+ end
20
+
21
+ def project_dir
22
+ ENV['BUILDKITE_BUILD_CHECKOUT_PATH']
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,29 @@
1
+ module KnapsackPro
2
+ module Config
3
+ module CI
4
+ class Circle < Base
5
+ def node_total
6
+ ENV['CIRCLE_NODE_TOTAL']
7
+ end
8
+
9
+ def node_index
10
+ ENV['CIRCLE_NODE_INDEX']
11
+ end
12
+
13
+ def commit_hash
14
+ ENV['CIRCLE_SHA1']
15
+ end
16
+
17
+ def branch
18
+ ENV['CIRCLE_BRANCH']
19
+ end
20
+
21
+ def project_dir
22
+ project_repo_name = ENV['CIRCLE_PROJECT_REPONAME']
23
+ "/home/ubuntu/#{project_repo_name}" if project_repo_name
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+
@@ -0,0 +1,28 @@
1
+ module KnapsackPro
2
+ module Config
3
+ module CI
4
+ class Semaphore < Base
5
+ def node_total
6
+ ENV['SEMAPHORE_THREAD_COUNT']
7
+ end
8
+
9
+ def node_index
10
+ index = ENV['SEMAPHORE_CURRENT_THREAD']
11
+ index.to_i - 1 if index
12
+ end
13
+
14
+ def commit_hash
15
+ ENV['REVISION']
16
+ end
17
+
18
+ def branch
19
+ ENV['BRANCH_NAME']
20
+ end
21
+
22
+ def project_dir
23
+ ENV['SEMAPHORE_PROJECT_DIR']
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,111 @@
1
+ module KnapsackPro
2
+ module Config
3
+ class Env
4
+ class << self
5
+ def ci_node_total
6
+ (ENV['KNAPSACK_PRO_CI_NODE_TOTAL'] ||
7
+ ci_env_for(:node_total) ||
8
+ 1).to_i
9
+ end
10
+
11
+ def ci_node_index
12
+ (ENV['KNAPSACK_PRO_CI_NODE_INDEX'] ||
13
+ ci_env_for(:node_index) ||
14
+ 0).to_i
15
+ end
16
+
17
+ def commit_hash
18
+ ENV['KNAPSACK_PRO_COMMIT_HASH'] ||
19
+ ci_env_for(:commit_hash)
20
+ end
21
+
22
+ def branch
23
+ ENV['KNAPSACK_PRO_BRANCH'] ||
24
+ ci_env_for(:branch)
25
+ end
26
+
27
+ def project_dir
28
+ ENV['KNAPSACK_PRO_PROJECT_DIR'] ||
29
+ ci_env_for(:project_dir)
30
+ end
31
+
32
+ def test_file_pattern
33
+ ENV['KNAPSACK_PRO_TEST_FILE_PATTERN']
34
+ end
35
+
36
+ def repository_adapter
37
+ ENV['KNAPSACK_PRO_REPOSITORY_ADAPTER']
38
+ end
39
+
40
+ def recording_enabled
41
+ ENV['KNAPSACK_PRO_RECORDING_ENABLED']
42
+ end
43
+
44
+ def recording_enabled?
45
+ recording_enabled == 'true'
46
+ end
47
+
48
+ def endpoint
49
+ env_name = 'KNAPSACK_PRO_ENDPOINT'
50
+ return ENV[env_name] if ENV[env_name]
51
+
52
+ case mode
53
+ when :development
54
+ 'http://api.knapsackpro.dev:3000'
55
+ when :test
56
+ 'http://api-staging.knapsackpro.com'
57
+ when :production
58
+ 'http://api.knapsackpro.com'
59
+ else
60
+ required_env(env_name)
61
+ end
62
+ end
63
+
64
+ def test_suite_token
65
+ required_env('KNAPSACK_PRO_TEST_SUITE_TOKEN')
66
+ end
67
+
68
+ def test_suite_token_rspec
69
+ ENV['KNAPSACK_PRO_TEST_SUITE_TOKEN_RSPEC']
70
+ end
71
+
72
+ def test_suite_token_minitest
73
+ ENV['KNAPSACK_PRO_TEST_SUITE_TOKEN_MINITEST']
74
+ end
75
+
76
+ def test_suite_token_cucumber
77
+ ENV['KNAPSACK_PRO_TEST_SUITE_TOKEN_CUCUMBER']
78
+ end
79
+
80
+ def mode
81
+ mode = ENV['KNAPSACK_PRO_MODE']
82
+ return :production if mode.nil?
83
+ mode = mode.to_sym
84
+ if [:development, :test, :production].include?(mode)
85
+ mode
86
+ else
87
+ raise ArgumentError.new('Wrong mode name')
88
+ end
89
+ end
90
+
91
+ def ci_env_for(env_name)
92
+ value = nil
93
+ ci_list = KnapsackPro::Config::CI.constants - [:Base]
94
+ ci_list.each do |ci_name|
95
+ ci_class = Object.const_get("KnapsackPro::Config::CI::#{ci_name}")
96
+ ci = ci_class.new
97
+ value = ci.send(env_name)
98
+ break unless value.nil?
99
+ end
100
+ value
101
+ end
102
+
103
+ private
104
+
105
+ def required_env(env_name)
106
+ ENV[env_name] || raise("Missing environment variable #{env_name}")
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,15 @@
1
+ module KnapsackPro
2
+ class LoggerWrapper
3
+ def initialize(logger)
4
+ @logger = ::ActiveSupport::TaggedLogging.new(logger)
5
+ end
6
+
7
+ private
8
+
9
+ attr_reader :logger
10
+
11
+ def method_missing(m, *args, &block)
12
+ logger.tagged('knapsack_pro') { logger.send(m, *args, &block) }
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,25 @@
1
+ module KnapsackPro
2
+ class Presenter
3
+ class << self
4
+ def global_time
5
+ global_time = pretty_seconds(KnapsackPro.tracker.global_time)
6
+ "Global time execution for tests: #{global_time}"
7
+ end
8
+
9
+ def pretty_seconds(seconds)
10
+ sign = ''
11
+
12
+ if seconds < 0
13
+ seconds = seconds*-1
14
+ sign = '-'
15
+ end
16
+
17
+ return "#{sign}#{seconds}s" if seconds.abs < 1
18
+
19
+ time = Time.at(seconds).gmtime.strftime('%Hh %Mm %Ss')
20
+ time_without_zeros = time.gsub(/00(h|m|s)/, '').strip
21
+ sign + time_without_zeros
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,20 @@
1
+ module KnapsackPro
2
+ class Report
3
+ def self.save
4
+ repository_adapter = KnapsackPro::RepositoryAdapterInitiator.call
5
+ action = KnapsackPro::Client::API::V1::BuildSubsets.create(
6
+ commit_hash: repository_adapter.commit_hash,
7
+ branch: repository_adapter.branch,
8
+ node_total: KnapsackPro::Config::Env.ci_node_total,
9
+ node_index: KnapsackPro::Config::Env.ci_node_index,
10
+ test_files: KnapsackPro.tracker.to_a,
11
+ )
12
+ connection = KnapsackPro::Client::Connection.new(action)
13
+ response = connection.call
14
+ if connection.success?
15
+ raise ArgumentError.new(response) if connection.errors?
16
+ KnapsackPro.logger.info('Saved time execution report on API server.')
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,12 @@
1
+ module KnapsackPro
2
+ class RepositoryAdapterInitiator
3
+ def self.call
4
+ case KnapsackPro::Config::Env.repository_adapter
5
+ when 'git'
6
+ KnapsackPro::RepositoryAdapters::GitAdapter.new
7
+ else
8
+ KnapsackPro::RepositoryAdapters::EnvAdapter.new
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,14 @@
1
+ module KnapsackPro
2
+ module RepositoryAdapters
3
+ class BaseAdapter
4
+ def commit_hash
5
+ raise NotImplementedError
6
+ end
7
+
8
+ def branch
9
+ raise NotImplementedError
10
+ end
11
+ end
12
+ end
13
+ end
14
+
@@ -0,0 +1,13 @@
1
+ module KnapsackPro
2
+ module RepositoryAdapters
3
+ class EnvAdapter < BaseAdapter
4
+ def commit_hash
5
+ KnapsackPro::Config::Env.commit_hash
6
+ end
7
+
8
+ def branch
9
+ KnapsackPro::Config::Env.branch
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,19 @@
1
+ module KnapsackPro
2
+ module RepositoryAdapters
3
+ class GitAdapter < BaseAdapter
4
+ def commit_hash
5
+ `git -C "#{working_dir}" rev-parse HEAD`.strip
6
+ end
7
+
8
+ def branch
9
+ `git -C "#{working_dir}" rev-parse --abbrev-ref HEAD`.strip
10
+ end
11
+
12
+ private
13
+
14
+ def working_dir
15
+ KnapsackPro::Config::Env.project_dir
16
+ end
17
+ end
18
+ end
19
+ end