diff --git a/.envrc b/.envrc index dfcfb3b..fc2facb 100644 --- a/.envrc +++ b/.envrc @@ -20,14 +20,14 @@ export K_SOUP_COV_DO=true # Means you want code coverage # Available formats are html, xml, rcov, lcov, json, tty export K_SOUP_COV_COMMAND_NAME="RSpec Coverage" export K_SOUP_COV_FORMATTERS="html,tty" -export K_SOUP_COV_MIN_BRANCH=50 # Means you want to enforce X% branch coverage -export K_SOUP_COV_MIN_LINE=60 # Means you want to enforce X% line coverage +export K_SOUP_COV_MIN_BRANCH=79 # Means you want to enforce X% branch coverage +export K_SOUP_COV_MIN_LINE=98 # Means you want to enforce X% line coverage export K_SOUP_COV_MIN_HARD=true # Means you want the build to fail if the coverage thresholds are not met export K_SOUP_COV_MULTI_FORMATTERS=true export MAX_ROWS=1 # Setting for simplecov-console gem for tty output, limits to the worst N rows of bad coverage # Internal Debugging Controls -export DEBUG=false # do not allow byebug statements (override in .env.local) +export DEBUG=true # do not allow byebug statements (override in .env.local) # .env would override anything in this file, if `dotenv` is uncommented below. # .env is a DOCKER standard, and if we use it, it would be in deployed, or DOCKER, environments, diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 538e679..377a070 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -1,8 +1,8 @@ name: Test Coverage env: - K_SOUP_COV_MIN_BRANCH: 50 - K_SOUP_COV_MIN_LINE: 60 + K_SOUP_COV_MIN_BRANCH: 79 + K_SOUP_COV_MIN_LINE: 98 K_SOUP_COV_MIN_HARD: true K_SOUP_COV_DO: true K_SOUP_COV_COMMAND_NAME: "RSpec Coverage" diff --git a/.gitignore b/.gitignore index ef31709..9a1c702 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,6 @@ /.idea/ # Packaging Artifacts -*.gem +/pkg/*.gem gemfiles/*.gemfile.lock Appraisal.*.gemfile.lock diff --git a/.rubocop.yml b/.rubocop.yml index 6de99cd..7b09f96 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,2 +1,11 @@ inherit_gem: rubocop-lts: config/rubygem_rspec.yml + +RSpec/NestedGroups: + Enabled: false + +RSpec/MultipleMemoizedHelpers: + Enabled: false + +RSpec/ExampleLength: + Enabled: false diff --git a/CHANGELOG.md b/CHANGELOG.md index 32a8a32..97c1d57 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,9 +22,9 @@ Please file a bug if you notice a violation of semantic versioning. ### Fixed ### Removed -## [1.0.0] - 2025-01-23 ([tag][1.0.0t]) -- Line Coverage: 60.34% (35 / 58) -- Branch Coverage: 50.0% (6 / 12) +## [1.0.0] - 2025-02-23 ([tag][1.0.0t]) +- COVERAGE: 98.63% -- 72/73 lines in 4 files +- BRANCH COVERAGE: 79.17% -- 19/24 branches in 4 files - 55.56% documented ### Added - Initial release diff --git a/Gemfile b/Gemfile index 1c99398..00bfc49 100644 --- a/Gemfile +++ b/Gemfile @@ -17,7 +17,7 @@ gemspec platform :mri do # Debugging - Ensure ENV["DEBUG"] == "true" to use debuggers within spec suite - if ruby_version < Gem::Version.new("2.7") + if ruby_version < Gem::Version.create("2.7") # Use byebug in code gem "byebug", ">= 11" else diff --git a/README.md b/README.md index e9cbc86..8757959 100644 --- a/README.md +++ b/README.md @@ -30,11 +30,12 @@ [![Donate to my FLOSS or refugee efforts at ko-fi.com][🖇kofi-img]][🖇kofi] [![Donate to my FLOSS or refugee efforts using Patreon][🖇patreon-img]][🖇patreon] -A ruby script, and rake task, to generate SHA-256 and SHA-512 checksums of RubyGem libraries, shipped as a RubyGem. +A ruby shell script, and rake task, to generate SHA-256 and SHA-512 checksums of RubyGem libraries, +shipped as a RubyGem. You may be familiar with the standard rake task `build:checksum` from RubyGems. This gem ships an improved version as `build:checksums`, based on the -[RubyGems pull request](https://github.com/rubygems/rubygems/pull/6022) I started in October 2022. +[RubyGems pull request][🔒️rubygems-checksums-pr] I started in October 2022. ```shell rake build:checksums @@ -88,11 +89,30 @@ gem install gem_checksums ## Usage -You may be familiar with the standard rake task `build:checksum` from RubyGems. -This gem ships an improved version as `build:checksums`, based on the -[RubyGems pull request and discussion here][🔒️rubygems-checksums-pr]. +Once installed you can use the shell script without any changes to your code. + +```shell +# prepend with `bundle exec` if gem was added to Gemfile instead of installed globally +gem_checksums +``` + +However, if you want to use the bundled rake task you'll need to add it to your Rakefile first. + +```ruby +begin + require "gem_checksums" + GemChecksums.install_tasks +rescue LoadError + task("build:checksums") do + warn("gem_checksums is not available") + end +end +``` + +Then you can do: ```shell +# prepend with `bundle exec` if gem was added to Gemfile instead of installed globally rake build:checksums ``` @@ -100,20 +120,106 @@ It is different from, and improves on, the standard rake task in that it: - does various checks to ensure the generated checksums will be valid - does `git commit` the generated checksums -You can alternatively use the shell script if `rake## 🔐 Security +### ENV variables + +Behavior can be controlled by ENV variables! + +- `GEM_CHECKSUMS_GIT_DRY_RUN` default value is `false` + - when `true` the `git commit` command will run with `--dry-run` flag + - when `true` the checksum files will be unstaged and deleted +- `GEM_CHECKSUMS_CHECKSUMS_DIR` default value is `checksums` (relative path) + - this directory will be created, relative to current working directory, if not present +- `GEM_CHECKSUMS_PACKAGE_DIR` default value is `pkg` (relative path) + - this directory will be searched for the latest gem package to generate checksums for + +### ARGV + +If an argument is provided to the rake task it should be the path to +a specific `.gem` package you want to generate checksums for. + +The script version does not accept arguments, and should be controlled by the ENV variables if needed. + +### How To: Release gem with checksums generated by `gem_checksums` + +Generating checksums makes sense when you are building and releasing a gem, so how does it fit into that process? + +NOTE: This is an example process which assumes your project has bundler binstubs, and a version.rb file, +with notes for `zsh` and `bash` shells. + +1. Run `bin/setup && bin/rake` as a tests, coverage, & linting sanity check +2. Update the version number in `version.rb` +3. Run `bin/setup && bin/rake` again as a secondary check, and to update `Gemfile.lock` +4. Run `git commit -am "🔖 Prepare release v"` to commit the changes +5. Run `git push` to trigger the final CI pipeline before release, & merge PRs + - NOTE: Remember to [check your project's CI][🧪build]! +6. Run `export GIT_TRUNK_BRANCH_NAME="$(git remote show origin | grep 'HEAD branch' | cut -d ' ' -f5)" && echo $GIT_TRUNK_BRANCH_NAME` +7. Run `git checkout $GIT_TRUNK_BRANCH_NAME` +8. Run `git pull origin $GIT_TRUNK_BRANCH_NAME` to ensure you will release the latest trunk code +9. Set `SOURCE_DATE_EPOCH` so `rake build` and `rake release` use same timestamp, and generate same checksums + - Run `export SOURCE_DATE_EPOCH=$EPOCHSECONDS && echo $SOURCE_DATE_EPOCH` + - If the echo above has no output, then it didn't work. + - Note that you'll need the `zsh/datetime` module, if running `zsh`. + - In `bash` you can use `date +%s` instead, i.e. `export SOURCE_DATE_EPOCH=$(date +%s) && echo $SOURCE_DATE_EPOCH` +10. Run `bundle exec rake build` +11. Run `gem_checksums` (from this gem, and added to path in .envrc, + more context [1][🔒️rubygems-checksums-pr] and [2][🔒️rubygems-guides-pr]) to create SHA-256 and SHA-512 checksums + - Checksums will be committed automatically by the script, but not pushed +12. Run `bundle exec rake release` which will create a git tag for the version, + push git commits and tags, and push the `.gem` file to [rubygems.org][💎rubygems] + +[🧪build]: https://github.com/pboling/gem_checksums/actions +[💎rubygems]: https://rubygems.org +[🔒️rubygems-security-guide]: https://guides.rubygems.org/security/#building-gems +[🔒️rubygems-checksums-pr]: https://github.com/rubygems/rubygems/pull/6022 +[🔒️rubygems-guides-pr]: https://github.com/rubygems/guides/pull/325 + +### Too many steps? + +If you don't follow the steps above you'll end up seeing this error: + +``` +WARNING: Build time not provided via environment variable SOURCE_DATE_EPOCH. + To ensure consistent SHA-256 & SHA-512 checksums, + you must set this environment variable *before* building the gem. + +IMPORTANT: After setting the build time, you *must re-build the gem*, i.e. bundle exec rake build, or gem build. + +How to set the build time: + +In zsh shell: + - export SOURCE_DATE_EPOCH=$EPOCHSECONDS && echo $SOURCE_DATE_EPOCH + - If the echo above has no output, then it didn't work. + - Note that you'll need the `zsh/datetime` module enabled. + +In fish shell: + - set -x SOURCE_DATE_EPOCH (date +%s) + - echo $SOURCE_DATE_EPOCH + +In bash shell: + - export SOURCE_DATE_EPOCH=$(date +%s) && echo $SOURCE_DATE_EPOCH` +``` + +Just do what it says! + +## 🔐 Security See [SECURITY.md][🔐security]. ## 🤝 Contributing If you need some ideas of where to help, you could work on adding more code coverage, -or if it is already 💯 (see [below](#code-coverage)) then check [issues][🤝issues], or [PRs][🤝pulls], +or if it is already 💯 (see [below](#code-coverage)) check TODOs (see [below](#todos)), +or check [issues][🤝issues], or [PRs][🤝pulls], or use the gem and think about how it could be better. We [![Keep A Changelog][📗keep-changelog-img]][📗keep-changelog] so if you make changes, remember to update it. See [CONTRIBUTING.md][🤝contributing] for more detailed instructions. +### TODOs + +- [ ] Prepend `rake build` task with check for `SOURCE_DATE_EPOCH` environment variable, and raise error if not set. + ### Code Coverage [![Coverage Graph][🔑codecov-g♻️]][🔑codecov] diff --git a/lib/gem_checksums.rb b/lib/gem_checksums.rb index 7023a12..a546a4e 100644 --- a/lib/gem_checksums.rb +++ b/lib/gem_checksums.rb @@ -22,6 +22,9 @@ class Error < StandardError; end VERSION_REGEX = /((\d+\.\d+\.\d+)([-.][0-9A-Za-z-]+)*)(?=\.gem)/ RUNNING_AS = File.basename($PROGRAM_NAME) BUILD_TIME_ERROR_MESSAGE = "Environment variable SOURCE_DATE_EPOCH must be set. You'll need to rebuild the gem. See gem_checksums/README.md" + GIT_DRY_RUN_ENV = ENV.fetch("GEM_CHECKSUMS_GIT_DRY_RUN", "false").casecmp("true") == 0 + CHECKSUMS_DIR = ENV.fetch("GEM_CHECKSUMS_CHECKSUMS_DIR", "checksums") + PACKAGE_DIR = ENV.fetch("GEM_CHECKSUMS_PACKAGE_DIR", "pkg") # Make this gem's rake tasks available in your Rakefile: # @@ -37,9 +40,11 @@ def install_tasks # NOTE: SOURCE_DATE_EPOCH must be set in your environment prior to building the gem. # This ensures that the gem build, and the gem checksum will use the same timestamp, # and thus will match the SHA-256 checksum generated for every gem on Rubygems.org. - def generate + def generate(git_dry_run: false) build_time = ENV.fetch("SOURCE_DATE_EPOCH", "") build_time_missing = !(build_time =~ /\d{10,}/) + git_dry_run_flag = (git_dry_run || GIT_DRY_RUN_ENV) ? "--dry-run" : nil + warn("Will run git commit with --dry-run") if git_dry_run_flag if build_time_missing warn( @@ -83,9 +88,11 @@ def generate gem_pkg = File.join(gem_path_parts) puts "Looking for: #{gem_pkg.inspect}" gems = Dir[gem_pkg] + raise Error, "Unable to find gem #{gem_pkg}" if gems.empty? + puts "Found: #{gems.inspect}" else - gem_pkgs = File.join("pkg", "*.gem") + gem_pkgs = File.join(PACKAGE_DIR, "*.gem") puts "Looking for: #{gem_pkgs.inspect}" gems = Dir[gem_pkgs] raise Error, "Unable to find gems #{gem_pkgs}" if gems.empty? @@ -103,21 +110,33 @@ def generate # SHA-512 digest is 8 64-bit words digest512_64bit = Digest::SHA512.new.hexdigest(pkg_bits) - digest512_64bit_path = "checksums/#{gem_name}.sha512" + digest512_64bit_path = "#{CHECKSUMS_DIR}/#{gem_name}.sha512" + Dir.mkdir(CHECKSUMS_DIR) unless Dir.exist?(CHECKSUMS_DIR) File.write(digest512_64bit_path, digest512_64bit) # SHA-256 digest is 8 32-bit words digest256_32bit = Digest::SHA256.new.hexdigest(pkg_bits) - digest256_32bit_path = "checksums/#{gem_name}.sha256" + digest256_32bit_path = "#{CHECKSUMS_DIR}/#{gem_name}.sha256" File.write(digest256_32bit_path, digest256_32bit) version = gem_name[VERSION_REGEX] - git_cmd = <<-GIT_MSG -git add checksums/* && \ -git commit -m "🔒️ Checksums for v#{version}" + git_cmd = <<-GIT_MSG.rstrip +git add #{CHECKSUMS_DIR}/* && \ +git commit #{git_dry_run_flag} -m "🔒️ Checksums for v#{version}" GIT_MSG + if git_dry_run_flag + git_cmd += <<-CLEANUP_MSG + && \ +echo "Cleaning up in dry run mode" && \ +git reset #{digest512_64bit_path} && \ +git reset #{digest256_32bit_path} && \ +rm -f #{digest512_64bit_path} && \ +rm -f #{digest256_32bit_path} + CLEANUP_MSG + end + puts <<-RESULTS [ GEM: #{gem_name} ] [ VERSION: #{version} ] @@ -132,10 +151,16 @@ def generate #{git_cmd} RESULTS - # This will replace the current process with the git process, and exit. - # Within the generate method, Ruby code placed after the `exec` *will not be run*: - # See: https://www.akshaykhot.com/call-shell-commands-in-ruby - exec(git_cmd) + if git_dry_run_flag + %x{#{git_cmd}} + else + # `exec` will replace the current process with the git process, and exit. + # Within the generate method, Ruby code placed after the `exec` *will not be run*: + # See: https://www.akshaykhot.com/call-shell-commands-in-ruby + # But we can't exit the process when testing from RSpec, + # since that would exit the parent RSpec process + exec(git_cmd) + end end module_function :generate end diff --git a/spec/config/byebug.rb b/spec/config/byebug.rb index 8acba80..ae3fbaf 100644 --- a/spec/config/byebug.rb +++ b/spec/config/byebug.rb @@ -1 +1,7 @@ -require "byebug" if VersionGem::Ruby.gte_minimum_version?("2.7") && ENV.fetch("DEBUG", "false").casecmp?("true") +if ENV.fetch("DEBUG", "false").casecmp("true") == 0 + if VersionGem::Ruby.gte_minimum_version?("2.7") + require "debug" + else + require "byebug" + end +end diff --git a/spec/gem_checksums/tasks_spec.rb b/spec/gem_checksums/tasks_spec.rb new file mode 100644 index 0000000..7b96067 --- /dev/null +++ b/spec/gem_checksums/tasks_spec.rb @@ -0,0 +1,135 @@ +# frozen_string_literal: true + +require "rake" + +RSpec.describe "rake build:checksums" do # rubocop:disable RSpec/DescribeClass + subject(:build_checksums) { invoke } + + include_context "with rake", "gem_checksums" + + context "with SOURCE_DATE_EPOCH set" do + before do + stub_env("SOURCE_DATE_EPOCH" => "1738472935") + end + + context "when running as Rake without arguments" do + before do + stub_const("GemChecksums::RUNNING_AS", "rake") + stub_const("ARGV", []) + end + + it "raises an error" do + block_is_expected.to raise_error(GemChecksums::Error, "Unable to find gems pkg/*.gem") + end + + context "when good package directory" do + let(:pkg_dir) { "spec/support/fixtures" } + + before do + stub_const("GemChecksums::RUNNING_AS", "rake") + stub_const("GemChecksums::PACKAGE_DIR", pkg_dir) + end + + it "does not raise an error" do + block_is_expected.to not_raise_error + end + + context "with output" do + it "prints information" do + block_is_expected.to output(<<-CHECKSUMS_OUTPUT).to_stdout +Looking for: "spec/support/fixtures/*.gem" +Found: 1 gems; latest is gem_checksums-1.0.0.gem +[ GEM: gem_checksums-1.0.0.gem ] +[ VERSION: 1.0.0 ] +[ GEM PKG LOCATION: spec/support/fixtures/gem_checksums-1.0.0.gem ] +[ CHECKSUM SHA-256: 8e35e0a7cae7fab47c0b7e3d3e81d38294151138bcfcfe8d4aa2e971ff302339 ] +[ CHECKSUM SHA-512: 8b18f8422f22a5a3b301deeaeb8b4e669dfc8033a63c1b5fbfc787af59f60e9df7f920ae2c48ede27ef7d09f0b025f63fc2474a842d54d8f148b266eac4bfa94 ] +[ CHECKSUM SHA-256 PATH: checksums/gem_checksums-1.0.0.gem.sha256 ] +[ CHECKSUM SHA-512 PATH: checksums/gem_checksums-1.0.0.gem.sha512 ] + +... Running ... + +git add checksums/* && git commit --dry-run -m "🔒️ Checksums for v1.0.0" && echo "Cleaning up in dry run mode" && git reset checksums/gem_checksums-1.0.0.gem.sha512 && git reset checksums/gem_checksums-1.0.0.gem.sha256 && rm -f checksums/gem_checksums-1.0.0.gem.sha512 && rm -f checksums/gem_checksums-1.0.0.gem.sha256 + + CHECKSUMS_OUTPUT + end + end + end + end + + context "when running as Rake with good arguments" do + let(:gem_fixture) { "spec/support/fixtures/gem_checksums-1.0.0.gem" } + + before do + stub_const("GemChecksums::RUNNING_AS", "rake") + stub_const("ARGV", [gem_fixture]) + end + + it "does not raise an error" do + block_is_expected.to not_raise_error + end + + context "with output" do + it "prints information" do + block_is_expected.to output(<<-CHECKSUMS_OUTPUT).to_stdout +Looking for: "spec/support/fixtures/gem_checksums-1.0.0.gem" +Found: ["spec/support/fixtures/gem_checksums-1.0.0.gem"] +[ GEM: gem_checksums-1.0.0.gem ] +[ VERSION: 1.0.0 ] +[ GEM PKG LOCATION: spec/support/fixtures/gem_checksums-1.0.0.gem ] +[ CHECKSUM SHA-256: 8e35e0a7cae7fab47c0b7e3d3e81d38294151138bcfcfe8d4aa2e971ff302339 ] +[ CHECKSUM SHA-512: 8b18f8422f22a5a3b301deeaeb8b4e669dfc8033a63c1b5fbfc787af59f60e9df7f920ae2c48ede27ef7d09f0b025f63fc2474a842d54d8f148b266eac4bfa94 ] +[ CHECKSUM SHA-256 PATH: checksums/gem_checksums-1.0.0.gem.sha256 ] +[ CHECKSUM SHA-512 PATH: checksums/gem_checksums-1.0.0.gem.sha512 ] + +... Running ... + +git add checksums/* && git commit --dry-run -m "🔒️ Checksums for v1.0.0" && echo "Cleaning up in dry run mode" && git reset checksums/gem_checksums-1.0.0.gem.sha512 && git reset checksums/gem_checksums-1.0.0.gem.sha256 && rm -f checksums/gem_checksums-1.0.0.gem.sha512 && rm -f checksums/gem_checksums-1.0.0.gem.sha256 + + CHECKSUMS_OUTPUT + end + end + + context "with no checksums directory" do + let(:checksums_dir) { "snooty_checksums" } + + before do + stub_const("GemChecksums::CHECKSUMS_DIR", checksums_dir) + Dir.rmdir(checksums_dir) if Dir.exist?(checksums_dir) + end + + after do + Dir.rmdir(checksums_dir) if Dir.exist?(checksums_dir) + end + + it "creates the checksums directory" do + build_checksums + expect(Dir.exist?(checksums_dir)).to be true + end + end + end + + context "when running as Rake with bad arguments" do + let(:gem_fixture) { "spec/support/fixtures/unreal-1.0.0.gem" } + + before do + stub_const("GemChecksums::RUNNING_AS", "rake") + stub_const("ARGV", [gem_fixture]) + end + + it "raises an error" do + block_is_expected.to raise_error(GemChecksums::Error, "Unable to find gem #{gem_fixture}") + end + end + end + + context "without SOURCE_DATE_EPOCH set" do + before do + stub_env("SOURCE_DATE_EPOCH" => "") + end + + it "raises an error" do + block_is_expected.to raise_error(GemChecksums::Error, "Environment variable SOURCE_DATE_EPOCH must be set. You'll need to rebuild the gem. See gem_checksums/README.md") + end + end +end diff --git a/spec/gem_checksums_spec.rb b/spec/gem_checksums_spec.rb index 0936504..9727cda 100644 --- a/spec/gem_checksums_spec.rb +++ b/spec/gem_checksums_spec.rb @@ -36,6 +36,10 @@ describe "::install_tasks" do subject(:install_tasks) { described_class.install_tasks } + before do + Rake.application = Rake::Application.new + end + it "loads gem_checksums/tasks.rb" do # The order is important, spec will fail if wrong order block_is_expected.to not_raise_error & diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 27b635b..e1a1c2b 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -11,15 +11,24 @@ require_relative "config/rspec/rspec_core" require_relative "config/rspec/version_gem" +# Support +require_relative "support/shared_contexts/with_rake" + # Last thing before loading this gem is to set up code coverage begin # This does not require "simplecov", but require "kettle-soup-cover" # this next line has a side effect of running `.simplecov` - require "simplecov" if Kettle::Soup::Cover::DO_COV + require "simplecov" if defined?(Kettle) && Kettle::Soup::Cover::DO_COV rescue LoadError nil end # This gem require "gem_checksums" + +RSpec.configure do |config| + config.before do + stub_const("GemChecksums::GIT_DRY_RUN_ENV", true) + end +end diff --git a/spec/support/fixtures/gem_checksums-1.0.0.gem b/spec/support/fixtures/gem_checksums-1.0.0.gem new file mode 100644 index 0000000..9e2bebc Binary files /dev/null and b/spec/support/fixtures/gem_checksums-1.0.0.gem differ diff --git a/spec/support/shared_contexts/with_rake.rb b/spec/support/shared_contexts/with_rake.rb new file mode 100644 index 0000000..a6e2e0e --- /dev/null +++ b/spec/support/shared_contexts/with_rake.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +# Inspired by: https://thoughtbot.com/blog/test-rake-tasks-like-a-boss +# This version doesn't require a Rails app! +require "rake" + +RSpec.shared_context("with rake") do |task_base_name| + let(:rake_app) { Rake::Application.new } + let(:task_name) { self.class.top_level_description.sub(/\Arake /, "") } + let(:task_dir) { "lib/gem_checksums/rakelib" } + let(:task_args) { [] } + let(:invoke) { rake_task.invoke(*task_args) } + let(:rakelib) { File.join(__dir__, "..", "..", "..", task_dir) } + let(:rake_task) { Rake::Task[task_name] } + + include_context "with stubbed env" + + def loaded_files_excluding_current_rake_file(task_base_name) + $".reject { |file| file == File.join(rakelib, "#{task_base_name}.rake").to_s } + end + + before do + Rake.application = rake_app + Rake.application.rake_require(task_base_name, [rakelib], loaded_files_excluding_current_rake_file(task_base_name)) + end +end