knapsack_pro 8.1.3 → 8.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f23120ba50c0ecf87cb9812a1238debd7d723c59e87fd006b93e1bbed2077cc1
4
- data.tar.gz: 65758d5c380cb93332756af1ce05c1ad917d59340d5ca0e3c5fb563783b56059
3
+ metadata.gz: 5d8372acfe13485ea86eeb30fdd524624cfbe05a374e4c6671048f898ecb5e94
4
+ data.tar.gz: 56df068506c0271ee9b55e92a7659dd1176bbfd7eef716f17f5ac9e67cd003cd
5
5
  SHA512:
6
- metadata.gz: e54574fe5e0656917394d6246ab61f0b4840262907c35fff7c824f2a8ed854b9b072a30968d82230b4e6454d7683fdb095bba31a21bf4458361ea399e0a93653
7
- data.tar.gz: 8a5030dd9184aa6d85330f884d849efdc3037e43614736386c718c041170553fdf1aadfe00331f748f2111fec299bd9b8332aa5e14719c241f1761c02c3b8bf4
6
+ metadata.gz: ac7bff173d40c9c83aa0d1a1053890d216e4939970af363415a5462264bd7c07fd95afe5e61c00930669f3f1b6ccee678762763bb6b5066b2923db7307afc48c
7
+ data.tar.gz: 27488c936d5cea663853d319a4564476568abe774ecaa91a2eb7f83e168ac4c1221091fb6969ac4b44d383673a30c4b141591051ba81987f95af75fb066c95bb
data/.circleci/config.yml CHANGED
@@ -357,7 +357,6 @@ jobs:
357
357
  KNAPSACK_PRO_ENDPOINT: https://api-staging.knapsackpro.com
358
358
  KNAPSACK_PRO_TEST_SUITE_TOKEN_MINITEST: $KNAPSACK_PRO_TEST_SUITE_TOKEN_MINITEST
359
359
  KNAPSACK_PRO_RSPEC_DISABLED: true
360
- EXTRA_TEST_FILES_DELAY: 10
361
360
  - image: cimg/postgres:14.7
362
361
  environment:
363
362
  POSTGRES_DB: rails-app-with-knapsack_pro_test
@@ -404,7 +403,6 @@ jobs:
404
403
  KNAPSACK_PRO_ENDPOINT: https://api-staging.knapsackpro.com
405
404
  KNAPSACK_PRO_TEST_SUITE_TOKEN_MINITEST: $KNAPSACK_PRO_TEST_SUITE_TOKEN_MINITEST
406
405
  KNAPSACK_PRO_RSPEC_DISABLED: true
407
- EXTRA_TEST_FILES_DELAY: 10
408
406
  - image: cimg/postgres:14.7
409
407
  environment:
410
408
  POSTGRES_DB: rails-app-with-knapsack_pro_test
data/CHANGELOG.md CHANGED
@@ -2,10 +2,20 @@
2
2
 
3
3
  ### UNRELEASED
4
4
 
5
+ ### 8.2.0
6
+
7
+ * Avoid needing a shell to support Distroless Container Images (in most cases)
8
+
9
+ https://github.com/KnapsackPro/knapsack_pro-ruby/pull/299
10
+
11
+ https://github.com/KnapsackPro/knapsack_pro-ruby/compare/v8.1.3...v8.2.0
12
+
5
13
  ### 8.1.3
6
14
 
7
15
  * Update `changelog_uri` in gemspec
8
16
 
17
+ https://github.com/KnapsackPro/knapsack_pro-ruby/compare/v8.1.2...v8.1.3
18
+
9
19
  ### 8.1.2
10
20
 
11
21
  * Allow running RSpec with `--force-color` (and the default Split by Test Examples)
data/README.md CHANGED
@@ -72,6 +72,42 @@ bin/test
72
72
 
73
73
  Scripted tests can be found in the [Rails App With Knapsack Pro repository](https://github.com/KnapsackPro/rails-app-with-knapsack_pro/blob/master/bin/knapsack_pro_all.rb).
74
74
 
75
+ ### Distroless Container Images
76
+
77
+ Ensure that Knapsack Pro is written in a way that supports Distroless Container Images. Avoid using a shell when calling methods like [`Kernel.system`](https://rubyapi.org/3.4/o/kernel#method-i-system) or [`Kernel.exec`](https://rubyapi.org/3.4/o/kernel#method-i-exec).
78
+
79
+ ✅ Good - shell is not required
80
+
81
+ ```ruby
82
+ cmd = 'bundle exec rake knapsack_pro:something'
83
+ Kernel.system({ 'RAILS_ENV' => 'test' }, cmd)
84
+
85
+ cmd = ['bundle', 'exec', 'rake', 'knapsack_pro:example']
86
+ Kernel.system({ 'RAILS_ENV' => 'test' }, *cmd)
87
+ ```
88
+
89
+ ⛔️ Bad - shell is required
90
+
91
+ ```ruby
92
+ # Avoid embedding environment variables in the command string
93
+ cmd = 'RAILS_ENV=test bundle exec rake knapsack_pro:something'
94
+ Kernel.system(cmd)
95
+
96
+ # Avoid output redirection
97
+ cmd = 'program 2>/dev/null'
98
+ Kernel.system(cmd)
99
+
100
+ # Avoid using the pipe operator
101
+ cmd = 'program1 | program2'
102
+ Kernel.system(cmd)
103
+ ```
104
+
105
+ Use [Dockerfile](distroless/Dockerfile) to test if the code requires a shell:
106
+
107
+ ```bash
108
+ docker build -t test -f distroless/Dockerfile . && docker run --rm -it test
109
+ ```
110
+
75
111
  ### Publishing
76
112
 
77
113
  1. Move the changes listed in the `UNRELEASED` section of the `CHANGELOG.md` to the proper version
@@ -0,0 +1,37 @@
1
+ FROM cgr.dev/chainguard/ruby:latest-dev AS builder
2
+
3
+ WORKDIR /build
4
+ RUN <<RUN
5
+ cat <<GEMFILE > Gemfile
6
+ source "https://rubygems.org"
7
+ GEMFILE
8
+
9
+ gem install bundler
10
+ bundle config set --local path vendor/bundle
11
+ bundle install
12
+ RUN
13
+
14
+ RUN <<RUN
15
+ cat <<RAKEFILE > Rakefile
16
+ RAKEFILE
17
+
18
+ mkdir bin
19
+ cat <<CI > bin/ci
20
+ ENV['PATH'] =+ "#{Gem.user_dir}/bin"
21
+ require 'bundler/setup'
22
+
23
+ success = Kernel.system({ 'RAILS_ENV' => 'test' }, 'bundle --version') # ✅
24
+ raise "The command failed!" unless success
25
+
26
+ success = Kernel.system({ 'RAILS_ENV' => 'test' }, 'bundle --version 2>/dev/null') # ⛔️
27
+ raise "The command failed!" unless success
28
+
29
+ CI
30
+ RUN
31
+
32
+ FROM cgr.dev/chainguard/ruby:latest
33
+
34
+ WORKDIR /app
35
+ COPY --from=builder /home/nonroot/.local /home/nonroot/.local
36
+ COPY --from=builder /build /app
37
+ CMD ["bin/ci"]
@@ -24,14 +24,14 @@ module KnapsackPro
24
24
  KnapsackPro.logger.info("Generating RSpec test examples JSON report for slow test files to prepare it to be split by test examples (by individual test cases). Thanks to that, a single slow test file can be split across parallel CI nodes. Analyzing #{slow_test_files.size} slow test files.")
25
25
 
26
26
  # generate the RSpec JSON report in a separate process to not pollute the RSpec state
27
+ envs = {'RACK_ENV' => 'test', 'RAILS_ENV' => 'test'}
27
28
  cmd = [
28
- 'RACK_ENV=test',
29
- 'RAILS_ENV=test',
30
29
  KnapsackPro::Config::Env.rspec_test_example_detector_prefix,
31
- 'rake knapsack_pro:rspec_test_example_detector',
30
+ 'rake knapsack_pro:rspec_test_example_detector'
32
31
  ].join(' ')
33
- unless Kernel.system(cmd)
34
- raise "Could not generate JSON report for RSpec. Rake task failed when running #{cmd}"
32
+ unless Kernel.system(envs, cmd)
33
+ inline_cmd = envs.map { _1.join('=') }.join(' ') + ' ' + cmd
34
+ raise "Could not generate JSON report for RSpec. Rake task failed when running #{inline_cmd}"
35
35
  end
36
36
 
37
37
  # read the JSON report
@@ -44,26 +44,41 @@ module KnapsackPro
44
44
 
45
45
  def git_commit_authors
46
46
  if KnapsackPro::Config::Env.ci? && shallow_repository?
47
- command = 'git fetch --shallow-since "one month ago" --quiet 2>/dev/null'
47
+ command = 'git fetch --shallow-since "one month ago" --quiet'
48
48
  begin
49
49
  Timeout.timeout(5) do
50
- `#{command}`
50
+ Kernel.system(command, out: File::NULL, err: File::NULL)
51
51
  end
52
52
  rescue Timeout::Error
53
53
  KnapsackPro.logger.debug("Skip the `#{command}` command because it took too long.")
54
54
  end
55
55
  end
56
56
 
57
- `git log --since "one month ago" 2>/dev/null | git shortlog --summary --email 2>/dev/null`
57
+ IO.pipe do |log_r, log_w|
58
+ Kernel.system('git log --since "one month ago"', out: log_w, err: File::NULL)
59
+ log_w.close
60
+ IO.pipe do |shortlog_r, shortlog_w|
61
+ Kernel.system('git shortlog --summary --email', in: log_r, out: shortlog_w, err: File::NULL)
62
+ shortlog_w.close
63
+ shortlog_r.read
64
+ end
65
+ end
58
66
  end
59
67
 
60
68
  def git_build_author
61
- `git log --format="%aN <%aE>" -1 2>/dev/null`
69
+ IO.pipe do |r, w|
70
+ Kernel.system('git log --format="%aN <%aE>" -1', out: w, err: File::NULL)
71
+ w.close
72
+ r.read
73
+ end
62
74
  end
63
75
 
64
76
  def shallow_repository?
65
- result = `git rev-parse --is-shallow-repository 2>/dev/null`
66
- result.strip == 'true'
77
+ IO.pipe do |r, w|
78
+ Kernel.system('git rev-parse --is-shallow-repository', out: w, err: File::NULL)
79
+ w.close
80
+ r.read.strip == 'true'
81
+ end
67
82
  end
68
83
 
69
84
  def working_dir
@@ -15,10 +15,12 @@ module KnapsackPro
15
15
 
16
16
  KnapsackPro.tracker.set_prerun_tests(runner.test_file_paths)
17
17
 
18
- cmd = %Q[KNAPSACK_PRO_REGULAR_MODE_ENABLED=true KNAPSACK_PRO_TEST_SUITE_TOKEN=#{ENV['KNAPSACK_PRO_TEST_SUITE_TOKEN']} bundle exec spinach #{args} --features_path #{runner.test_dir} -- #{runner.stringify_test_file_paths}]
18
+ cmd = %Q[bundle exec spinach #{args} --features_path #{runner.test_dir} -- #{runner.stringify_test_file_paths}]
19
19
 
20
- Kernel.system(cmd)
21
- Kernel.exit(child_status.exitstatus) unless child_status.exitstatus.zero?
20
+ Kernel.exec({
21
+ 'KNAPSACK_PRO_REGULAR_MODE_ENABLED' => 'true',
22
+ 'KNAPSACK_PRO_TEST_SUITE_TOKEN' => ENV['KNAPSACK_PRO_TEST_SUITE_TOKEN']
23
+ }, cmd)
22
24
  end
23
25
  end
24
26
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module KnapsackPro
4
- VERSION = '8.1.3'
4
+ VERSION = '8.2.0'
5
5
  end
@@ -5,7 +5,7 @@ require 'knapsack_pro'
5
5
  namespace :knapsack_pro do
6
6
  namespace :queue do
7
7
  task :cucumber, [:cucumber_args] do |_, args|
8
- Kernel.exec("RAILS_ENV=test RACK_ENV=test #{$PROGRAM_NAME} 'knapsack_pro:queue:cucumber_go[#{args[:cucumber_args]}]'")
8
+ Kernel.exec({ 'RAILS_ENV' => 'test', 'RACK_ENV' => 'test' }, $PROGRAM_NAME, "knapsack_pro:queue:cucumber_go[#{args[:cucumber_args]}]")
9
9
  end
10
10
 
11
11
  task :cucumber_go, [:cucumber_args] do |_, args|
@@ -5,7 +5,7 @@ require 'knapsack_pro'
5
5
  namespace :knapsack_pro do
6
6
  namespace :queue do
7
7
  task :minitest, [:minitest_args] do |_, args|
8
- Kernel.exec("RAILS_ENV=test RACK_ENV=test #{$PROGRAM_NAME} 'knapsack_pro:queue:minitest_go[#{args[:minitest_args]}]'")
8
+ Kernel.exec({ 'RAILS_ENV' => 'test', 'RACK_ENV' => 'test' }, $PROGRAM_NAME, "knapsack_pro:queue:minitest_go[#{args[:minitest_args]}]")
9
9
  end
10
10
 
11
11
  task :minitest_go, [:minitest_args] do |_, args|
@@ -5,7 +5,7 @@ require 'knapsack_pro'
5
5
  namespace :knapsack_pro do
6
6
  namespace :queue do
7
7
  task :rspec, [:rspec_args] do |_, args|
8
- Kernel.exec("RAILS_ENV=test RACK_ENV=test #{$PROGRAM_NAME} 'knapsack_pro:queue:rspec_go[#{args[:rspec_args]}]'")
8
+ Kernel.exec({ 'RAILS_ENV' => 'test', 'RACK_ENV' => 'test' }, $PROGRAM_NAME, "knapsack_pro:queue:rspec_go[#{args[:rspec_args]}]")
9
9
  end
10
10
 
11
11
  task :rspec_go, [:rspec_args] do |_, args|
@@ -57,19 +57,36 @@ describe KnapsackPro::Adapters::RSpecAdapter do
57
57
 
58
58
  subject { described_class.test_file_cases_for(slow_test_files) }
59
59
 
60
- before do
61
- logger = instance_double(Logger)
62
- expect(KnapsackPro).to receive(:logger).and_return(logger)
63
- expect(logger).to receive(:info).with("Generating RSpec test examples JSON report for slow test files to prepare it to be split by test examples (by individual test cases). Thanks to that, a single slow test file can be split across parallel CI nodes. Analyzing 5 slow test files.")
60
+ context 'when the rake task to detect RSpec test examples succeeded' do
61
+ it 'returns test example paths for slow test files' do
62
+ logger = instance_double(Logger)
63
+ allow(KnapsackPro).to receive(:logger).and_return(logger)
64
+ allow(logger).to receive(:info)
65
+
66
+ cmd = 'bundle exec rake knapsack_pro:rspec_test_example_detector'
67
+ env = { 'RACK_ENV' => 'test', 'RAILS_ENV' => 'test' }
68
+ expect(Kernel).to receive(:system).with(env, cmd).and_return(true)
69
+
70
+ rspec_test_example_detector = instance_double(KnapsackPro::TestCaseDetectors::RSpecTestExampleDetector)
71
+ expect(KnapsackPro::TestCaseDetectors::RSpecTestExampleDetector).to receive(:new).and_return(rspec_test_example_detector)
72
+
73
+ test_file_example_paths = double
74
+ expect(rspec_test_example_detector).to receive(:test_file_example_paths).and_return(test_file_example_paths)
75
+
76
+ expect(subject).to eq test_file_example_paths
64
77
 
65
- cmd = 'RACK_ENV=test RAILS_ENV=test bundle exec rake knapsack_pro:rspec_test_example_detector'
66
- expect(Kernel).to receive(:system).with(cmd).and_return(cmd_result)
78
+ expect(logger).to have_received(:info).with("Generating RSpec test examples JSON report for slow test files to prepare it to be split by test examples (by individual test cases). Thanks to that, a single slow test file can be split across parallel CI nodes. Analyzing 5 slow test files.")
79
+ end
67
80
  end
68
81
 
69
- context 'when the rake task to detect RSpec test examples succeeded' do
70
- let(:cmd_result) { true }
82
+ context 'when KNAPSACK_PRO_RSPEC_TEST_EXAMPLE_DETECTOR_PREFIX is set with a custom environment variable' do
83
+ it 'calls Kernel.system using a single command including ENVs that is passed to a shell (does not work in a distroless environment)' do
84
+ stub_const('ENV', { 'KNAPSACK_PRO_RSPEC_TEST_EXAMPLE_DETECTOR_PREFIX' => 'CUSTOM_ENV_VAR=123 bundle exec' })
85
+
86
+ cmd = 'CUSTOM_ENV_VAR=123 bundle exec rake knapsack_pro:rspec_test_example_detector'
87
+ env = { 'RACK_ENV' => 'test', 'RAILS_ENV' => 'test' }
88
+ expect(Kernel).to receive(:system).with(env, cmd).and_return(true)
71
89
 
72
- it 'returns test example paths for slow test files' do
73
90
  rspec_test_example_detector = instance_double(KnapsackPro::TestCaseDetectors::RSpecTestExampleDetector)
74
91
  expect(KnapsackPro::TestCaseDetectors::RSpecTestExampleDetector).to receive(:new).and_return(rspec_test_example_detector)
75
92
 
@@ -81,9 +98,11 @@ describe KnapsackPro::Adapters::RSpecAdapter do
81
98
  end
82
99
 
83
100
  context 'when the rake task to detect RSpec test examples failed' do
84
- let(:cmd_result) { false }
85
-
86
101
  it do
102
+ cmd = 'bundle exec rake knapsack_pro:rspec_test_example_detector'
103
+ env = { 'RACK_ENV' => 'test', 'RAILS_ENV' => 'test' }
104
+ expect(Kernel).to receive(:system).with(env, cmd).and_return(false)
105
+
87
106
  expect { subject }.to raise_error(RuntimeError, 'Could not generate JSON report for RSpec. Rake task failed when running RACK_ENV=test RAILS_ENV=test bundle exec rake knapsack_pro:rspec_test_example_detector')
88
107
  end
89
108
  end
@@ -19,8 +19,8 @@ describe KnapsackPro::Runners::SpinachRunner do
19
19
 
20
20
  context 'when test files were returned by Knapsack Pro API' do
21
21
  let(:test_file_paths) { ['features/a.feature', 'features/b.feature'] }
22
- let(:stringify_test_file_paths) { test_file_paths.join(' ') }
23
22
  let(:test_dir) { 'fake-test-dir' }
23
+ let(:stringify_test_file_paths) { test_file_paths.join(' ') }
24
24
  let(:runner) do
25
25
  instance_double(described_class,
26
26
  test_dir: test_dir,
@@ -30,42 +30,19 @@ describe KnapsackPro::Runners::SpinachRunner do
30
30
  end
31
31
  let(:child_status) { double }
32
32
 
33
- before do
33
+ it do
34
34
  expect(KnapsackPro::Adapters::SpinachAdapter).to receive(:verify_bind_method_called)
35
35
 
36
36
  tracker = instance_double(KnapsackPro::Tracker)
37
37
  expect(KnapsackPro).to receive(:tracker).and_return(tracker)
38
38
  expect(tracker).to receive(:set_prerun_tests).with(test_file_paths)
39
39
 
40
- expect(Kernel).to receive(:system).with('KNAPSACK_PRO_REGULAR_MODE_ENABLED=true KNAPSACK_PRO_TEST_SUITE_TOKEN=spinach-token bundle exec spinach --custom-arg --features_path fake-test-dir -- features/a.feature features/b.feature')
41
-
42
- allow(described_class).to receive(:child_status).and_return(child_status)
43
- end
44
-
45
- after { subject }
46
-
47
- context 'when command exit with success code' do
48
- let(:exitstatus) { 0 }
40
+ expect(Kernel).to receive(:exec).with(
41
+ { 'KNAPSACK_PRO_REGULAR_MODE_ENABLED' => 'true', 'KNAPSACK_PRO_TEST_SUITE_TOKEN' => 'spinach-token' },
42
+ 'bundle exec spinach --custom-arg --features_path fake-test-dir -- features/a.feature features/b.feature'
43
+ )
49
44
 
50
- before do
51
- expect(child_status).to receive(:exitstatus).and_return(exitstatus)
52
- end
53
-
54
- it do
55
- expect(Kernel).not_to receive(:exit)
56
- end
57
- end
58
-
59
- context 'when command exit without success code' do
60
- let(:exitstatus) { 1 }
61
-
62
- before do
63
- expect(child_status).to receive(:exitstatus).twice.and_return(exitstatus)
64
- end
65
-
66
- it do
67
- expect(Kernel).to receive(:exit).with(exitstatus)
68
- end
45
+ subject
69
46
  end
70
47
  end
71
48
 
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: knapsack_pro
3
3
  version: !ruby/object:Gem::Version
4
- version: 8.1.3
4
+ version: 8.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - ArturT
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-05-09 00:00:00.000000000 Z
10
+ date: 2025-05-21 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: rake
@@ -216,6 +216,7 @@ files:
216
216
  - Rakefile
217
217
  - bin/knapsack_pro
218
218
  - bin/test
219
+ - distroless/Dockerfile
219
220
  - knapsack_pro.gemspec
220
221
  - lib/knapsack_pro.rb
221
222
  - lib/knapsack_pro/adapters/base_adapter.rb