-
Notifications
You must be signed in to change notification settings - Fork 30
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Use .knapsack_pro
directory for temporary files instead of the tmp
directory in the user's project directory
#155
Changes from 26 commits
a709b09
4f43966
e2b4f1d
1daee2c
0965a3d
988365f
65ce2c0
fc8ccb8
5f111a9
255cbad
fcbdeb9
2f350ce
98ef84a
2ef9649
6217728
97af123
8aeb0d1
2864bc1
3997b94
46c65be
4a060b0
2449713
28b1c62
1636994
eb9ab70
5b993f0
4ecff24
880f263
427abda
a7073c2
56df05c
5f12f1b
c81ca2e
390a1d3
f69065d
6a5036d
5d6e5b9
e21f0a3
7756183
b7c3b15
8b8cf7b
c96ab7d
217639f
2afdc5c
0a811c0
eca0920
a58724b
5b38f78
16dc997
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,7 +6,7 @@ class BaseAdapter | |
|
||
def self.adapter_bind_method_called_file | ||
adapter_name = self.to_s.gsub('::', '-') | ||
"#{KnapsackPro::Config::Env::TMP_DIR}/#{adapter_name}-bind_method_called_for_node_#{KnapsackPro::Config::Env.ci_node_index}.txt" | ||
"#{KnapsackPro::Config::TempFiles.temp_directory_path}/#{adapter_name}-bind_method_called_for_node_#{KnapsackPro::Config::Env.ci_node_index}.txt" | ||
end | ||
|
||
def self.slow_test_file?(adapter_class, test_file_path) | ||
|
@@ -39,14 +39,14 @@ def self.verify_bind_method_called | |
puts "\n\n" | ||
KnapsackPro.logger.error('-'*10 + ' Configuration error ' + '-'*50) | ||
KnapsackPro.logger.error("You forgot to call #{self}.bind method in your test runner configuration file. It is needed to record test files time execution. Please follow the installation guide to configure your project properly https://docs.knapsackpro.com/knapsack_pro-ruby/guide/") | ||
KnapsackPro.logger.error("If you already have #{self}.bind method added and you still see this error then one of your tests must had to delete tmp/knapsack_pro directory from the disk accidentally. Please ensure you do not remove tmp/knapsack_pro directory: https://knapsackpro.com/faq/question/why-all-test-files-have-01s-time-execution-for-my-ci-build-in-user-dashboard") | ||
KnapsackPro.logger.error("If you already have #{self}.bind method added and you still see this error then one of your tests must had to delete .knapsack_pro directory from the disk accidentally. Please ensure you do not remove .knapsack_pro directory: https://knapsackpro.com/faq/question/why-all-test-files-have-01s-time-execution-for-my-ci-build-in-user-dashboard") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Pretty sure "must had to delete" is ungrammatical. How about "must have deleted"? Also, I think we are missing "the" in "the .knapsack_pro directory" (2 occurrences) |
||
Kernel.exit(1) | ||
end | ||
end | ||
end | ||
|
||
def bind | ||
FileUtils.mkdir_p(KnapsackPro::Config::Env::TMP_DIR) | ||
FileUtils.mkdir_p(KnapsackPro::Config::TempFiles.temp_directory_path) | ||
File.write(self.class.adapter_bind_method_called_file, nil) | ||
|
||
if KnapsackPro::Config::Env.recording_enabled? | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
module KnapsackPro | ||
module Config | ||
class TempFiles | ||
def self.temp_directory_path | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Have you considered separating the ensuring the temp directory exists from returning the path? Or maybe memoizing the result or sth? Do you think there is a way to do this check once at some point, or would it always be succeptible to errors? The reason I'm wondering about this is that it seems that we are using this There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was thinking about it and I decided that checking if file exists on the disk is good enough. We already do some IO operations in queue mode like saving reports and it shouldn't have significant impact on performance. If I add memorization to class method then this causes random tests failures depending on the order of tests executed because class method memorization is preserved for each call of class method. For instance I could do: def self.temp_directory_path
@temp_directory_path ||=
begin
temp_files = new
temp_files.ensure_temp_directory_exists
temp_files.temp_directory_path
end
end and then when I run tests I get randomly different tests failures:
I could disable memorization with custom argument |
||
temp_files = new | ||
temp_files.ensure_temp_directory_exists | ||
temp_files.temp_directory_path | ||
end | ||
|
||
def ensure_temp_directory_exists | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need to keep this method and the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When I move code to private
def ensure_temp_directory_exists
unless File.exists?(gitignore_file_path)
create_temp_directory
create_gitignore_file
end
end
# relative to the directory where you run knapsack_pro gem (user's project)
def temp_directory_path
'.knapsack_pro'
end then I get error:
When I move code to
We call the following methods in class method temp_files.ensure_temp_directory_exists
temp_files.temp_directory_path There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, this is because we are trying to access the private method from outside the object. If we are adamant about the public method remaining a class method, we could change all the other methods to be private class methods. |
||
unless File.exists?(gitignore_file_path) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This method is deprecated in ruby 2.7.1, and doesn't exist in ruby 3.0.2. There is an #exist? method, but I'm not sure if they are effectively the same. I was dealing with ensuring a directory exists in my past side-project. I looked up the code and noticed I was using the In case it's helpful to you, here is how I handled a similar case back then: create_needed_dirs!
def create_needed_dirs!
create!('foo')
create!('bar')
end
def create!(directory)
return false if Dir.exist?(directory)
Dir.mkdir(directory) && true
end There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good catch. I've used |
||
create_temp_directory | ||
create_gitignore_file | ||
end | ||
end | ||
|
||
# relative to the directory where you run knapsack_pro gem (user's project) | ||
def temp_directory_path | ||
'.knapsack_pro' | ||
end | ||
|
||
private | ||
|
||
def create_temp_directory | ||
FileUtils.mkdir_p(temp_directory_path) | ||
end | ||
|
||
def gitignore_file_path | ||
File.join(temp_directory_path, '.gitignore') | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do I understand correctly, that we'll create our own Does it mean that there would now need to be the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yes.
No. Because we also ignore
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Cool, I hoped it works the way you described it. Great to know it does indeed! 💚 |
||
end | ||
|
||
def gitignore_file_content | ||
"# This directory is used by knapsack_pro gem for temporary files during tests runtime.\n" << | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think that "for storing temporary files" sounds more proper. |
||
"# Ignore all files, and do not commit this directory into your repository.\n" << | ||
"# Learn more at https://knapsackpro.com\n" << | ||
"*" | ||
end | ||
|
||
def create_gitignore_file | ||
File.open(gitignore_file_path, 'w+') do |f| | ||
f.write(gitignore_file_content) | ||
end | ||
end | ||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,7 +5,7 @@ def self.save | |
|
||
if test_files.empty? | ||
KnapsackPro.logger.warn("No test files were executed on this CI node.") | ||
KnapsackPro.logger.debug("When you use knapsack_pro regular mode then probably reason might be very narrowed tests list - you run only tests with specified tag and there are fewer test files with the tag than node total number.") | ||
KnapsackPro.logger.debug("When you use knapsack_pro Regular Mode, the reason for no tests executing might be a very narrow tests list. Most likely, you run only tests with a specified tag, and the number of test files with the tag was lower than the number of parallel CI nodes.") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice improvements. 👍 the last bit could be simplified to ", and there were fewer test files with the tag than parallel CI nodes." |
||
end | ||
|
||
create_build_subset(test_files) | ||
|
@@ -36,8 +36,7 @@ def self.save_node_queue_to_api | |
|
||
if test_files.empty? | ||
KnapsackPro.logger.warn("No test files were executed on this CI node.") | ||
KnapsackPro.logger.debug("When you use knapsack_pro queue mode then probably reason might be that CI node was started after the test files from the queue were already executed by other CI nodes. That is why this CI node has no test files to execute.") | ||
KnapsackPro.logger.debug("Another reason might be when your CI node failed in a way that prevented knapsack_pro to save time execution data to Knapsack Pro API and you have just tried to retry failed CI node but instead you got no test files to execute. In that case knapsack_pro don't know what tests should be executed here.") | ||
KnapsackPro.logger.debug("This CI node likely started work late after the test files were already executed by other CI nodes consuming the queue.") | ||
end | ||
|
||
measured_test_files = test_files | ||
|
@@ -46,9 +45,9 @@ def self.save_node_queue_to_api | |
|
||
if test_files.size > 0 && measured_test_files.size == 0 | ||
KnapsackPro.logger.warn("#{test_files.size} test files were executed on this CI node but the recorded time was lost due to:") | ||
KnapsackPro.logger.warn("1. Probably you have a code (i.e. RSpec hooks) that clears tmp directory in your project. Please ensure you do not remove the content of tmp/knapsack_pro/queue/ directory between tests run.") | ||
KnapsackPro.logger.warn("2. Another reason might be that you forgot to add Knapsack::Adapters::RSpecAdapter.bind in your rails_helper.rb or spec_helper.rb. Please follow the installation guide again: https://docs.knapsackpro.com/integration/") | ||
KnapsackPro.logger.warn("3. All your tests are empty test files, are pending tests or have syntax error and could not be executed hence no measured time execution by knapsack_pro.") | ||
KnapsackPro.logger.warn("1. Please ensure you do not remove the content of .knapsack_pro/queue/ directory between tests run.") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "the contents" I would add the before Do you think it's a good idea to mention the more general, main |
||
KnapsackPro.logger.warn("2. Ensure you added Knapsack::Adapters::RSpecAdapter.bind in your rails_helper.rb or spec_helper.rb. Please follow the installation guide again: https://docs.knapsackpro.com/integration/") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this should read "you've added" |
||
KnapsackPro.logger.warn("3. Another reason for this warning is that all your tests are empty test files, pending tests, or they have syntax errors, and the time execution was not recorded for them.") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would say "Another potential reason" to emphasize this being a hypothesis and not a statement |
||
end | ||
|
||
create_build_subset(test_files) | ||
|
@@ -80,7 +79,7 @@ def self.create_build_subset(test_files) | |
|
||
def self.queue_path | ||
queue_id = KnapsackPro::Config::Env.queue_id | ||
"#{KnapsackPro::Config::Env::TMP_DIR}/queue/#{queue_id}" | ||
"#{KnapsackPro::Config::TempFiles.temp_directory_path}/queue/#{queue_id}" | ||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -92,7 +92,7 @@ def set_defaults | |
end | ||
|
||
def tracker_dir_path | ||
"#{KnapsackPro::Config::Env::TMP_DIR}/tracker" | ||
"#{KnapsackPro::Config::TempFiles.temp_directory_path}/tracker" | ||
end | ||
|
||
def prerun_tests_report_path | ||
|
@@ -110,6 +110,7 @@ def save_prerun_tests_report(hash) | |
end | ||
|
||
def read_prerun_tests_report | ||
raise "Report #{prerun_tests_report_path} doest not exist on the disk. Most likely, it was removed accidentally. Please report the bug to the support team." unless File.exists?(prerun_tests_report_path) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe we can add a link here, or at least specify that we mean the Knapsack support team? |
||
JSON.parse(File.read(prerun_tests_report_path)) | ||
end | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
describe KnapsackPro::Config::TempFiles, :clear_tmp do | ||
describe '.temp_directory_path' do | ||
let(:gitignore_file_path) { '.knapsack_pro/.gitignore' } | ||
|
||
subject { described_class.temp_directory_path } | ||
|
||
it 'returns temporary directory path' do | ||
expect(subject).to eq '.knapsack_pro' | ||
end | ||
|
||
it 'creates .gitignore file' do | ||
expect(File.exists?(gitignore_file_path)).to be false | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. FYI there is a nice DSL for expectations like that https://relishapp.com/rspec/rspec-expectations/v/2-0/docs/matchers/expect-change e.g. (just wanted to share this in case you forgot about it, the current one is cool by all means) |
||
subject | ||
expect(File.exists?(gitignore_file_path)).to be true | ||
end | ||
|
||
it '.gitignore file has content' do | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do I understand correctly that the most important line in the file's content is the one with the asterisk ( |
||
subject | ||
expect(File.read(gitignore_file_path)).to include '# This directory is used by knapsack_pro gem for temporary files during tests runtime' | ||
end | ||
end | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this should read
user's project
. I would also change "folder" to "directory" (despite the duplication).