Background
- Dự án mình đang làm có một yêu cầu như sau: Viết một
rake task
thực hiện một chức năng xyz, nếu có lỗi validation thì hiển thị ra. Có khả năng thêm option--force
vào câu lệnh để thực hiện chức năng ngay cả khi có lỗi validation. - Viết xong, chạy task xyz đó và
rspecs
(phải viết cả unit test cho rake task) trên máy local ổn, nhưng khi commit lên git, CircleCi báo lỗi ở một vài cases. Rõ ràng là ở máy local không sao??? - Đây chỉ là một kinh nghiệm nho nhỏ mà đáng ra mình phải biết và làm từ đầu để tránh lỗi trên CircleCi.
Research
CircleCi báo lỗi thì đương nhiên phải bắt đầu điều tra nó trước.
Xem báo cáo của CircleCi, chỉ thấy báo lỗi ở một vài cases như đã nói ở trên, nhưng không hiểu vì sao lỗi, mình đã cố thiết lập sao cho giống với môi trường trên CircleCi, kể cả set RAILS_ENV=test
trên máy local, nhưng ko kết quả gì.
Xem thêm terminal log của CircleCi thì nó chạy rake như sau:
bundle exec rspec --color --require spec_helper --format RspecJunitFormatter --out /tmp/circle-junit.WOzJ9A5/rspec/rspec.xml 'spec/file1.rb' 'spec/file2.rb' ...
Chạy như này trên máy local thì đúng báo lỗi thật. Trong các cases của rspecs
thì đang chạy rake với chế độ silent, tức là không in gì ra log cả, tắt silent đi thì nhận được:
invalid option: --color
invalid option: --require
...
Vậy có nghĩa là những arguments trong câu lệnh chạy rspec
thì nó cũng pass luôn vào rake
. Có một vài cách để pass arguments vào rake
, cách hiện tại đang dùng là:
task :task_name, [:arg_name_1] => :environment do |t, args|
# lets dance!
end
Lúc đó cách chạy task sẽ là:
rake task_name[arg_name_1,arg2,arg3,...,argn]
Muốn sử dụng option kiểu --force
vào thì phải dùng Ruby OptionParser
để bắt nó:
require 'optparse'
option = OptionParser.new
option.banner = "Usage: rake task_name[options] -- -f[--force]"
option.on("-f", "--force") { |force| options[:force] = force }
Đây chính là điểm gây ra vấn đề, nếu không có cụm này thì thêm hàng đống option vào rake
vẫn chạy bình thường. Một khi đã dùng OptionParser
, phải quan tâm đến tất cả những option nào được/không được phép truyền vào. Nếu option truyền vào không có trong định nghĩa (option.on()
ở trên), nó sẽ báo lỗi invalid option
và dừng ngay tức khắc, chức năng không thực hiện được dẫn tới expect
không đúng như trong rspecs
đã viết, dẫn tới failures trên CircleCi.
Okay, vậy ở đây ta sẽ chỉ cho phép --force
và loại bỏ tất cả những options khác.
other_options = []
begin
option = OptionParser.new
option.banner = "Usage: rake task_name[options] -- -f[--force]"
option.on("-f", "--force") { |force| options[:force] = force }
rescue OptionParser::InvalidOption => e
other_options.concat e.args
end
Hãy bắt ngoại lệ OptionParser::InvalidOption
thì không một option thừa nào lọt được vào nữa, other_options
chỉ là array để lưu lại những option đó nếu bạn muốn in tất cả chúng ra báo cho người dùng.
Vậy là xong!
Conclusion
- Không phải cứ chạy test trước trên máy thì sẽ yên tâm là ngon lành trên CircleCi, hoặc bất cứ framework nào khác.
- Nên định nghĩa những arguments được phép dùng trong task và bắt tất cả những gì không hợp lệ, nếu không task sẽ dừng. Mình chưa có kinh nghiệm viết
rake task
nhiều, nên giờ mới ngộ ra điểm này. - Thank God It's Friday, quẩy đê!