From 9c7e76e72feb5fe4d6aeb6e15f1dd0d1c0e97351 Mon Sep 17 00:00:00 2001 From: James Couball Date: Mon, 11 Dec 2023 08:44:53 -0800 Subject: [PATCH] Update project to new build and documentation standards (#22) * Add CI build with Github Actions * Add a Github codeowners file * Update dependencies * Update rubocop configuration * Fix new Rubocop offenses * Use semverify instead of bump * Update Rakefile to standard for my projects * Update spec_helper to standard for my projects * Update YARD documentation to new standards * Update README.md project badges * Document the arg passed to the initialization block * Remove bundler as a direct dev dependency --- .github/CODEOWNERS | 4 + .github/workflows/main.yml | 64 +++++ .rubocop.yml | 28 ++- README.md | 29 ++- Rakefile | 91 +++++-- github_pages_rake_tasks.gemspec | 29 ++- lib/github_pages_rake_tasks/interface.rb | 3 - lib/github_pages_rake_tasks/publish_task.rb | 125 +++++++++- lib/github_pages_rake_tasks/state.rb | 251 ++++++++++++-------- spec/spec_helper.rb | 56 ++++- 10 files changed, 510 insertions(+), 170 deletions(-) create mode 100644 .github/CODEOWNERS create mode 100644 .github/workflows/main.yml diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..af0a916 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,4 @@ +# Defines who must review pull requests. +# See https://help.github.com/articles/about-codeowners/ for details. + +* @jcouball @main-branch/github_pages_rake_tasks-codeowners diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..8375950 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,64 @@ +name: CI Build + +on: + push: + branches: [ main ] + + pull_request: + branches: [ main ] + +jobs: + build: + continue-on-error: true + + strategy: + matrix: + ruby: ['3.0', '3.2', head, jruby-head] + operating-system: [ubuntu-latest] + include: + - ruby: '3.0' + operating-system: windows-latest + - ruby: jruby-head + operating-system: windows-latest + + name: Ruby ${{ matrix.ruby }} on ${{ matrix.operating-system }} + runs-on: ${{ matrix.operating-system }} + + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Initialize Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + bundler-cache: true + + - name: Run rake + run: bundle exec rake + + coverage: + needs: [ build ] + runs-on: ubuntu-latest + + name: Report test coverage to CodeClimate + + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Initialize Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: 3.1 + bundler-cache: true + + - name: Run tests + run: bundle exec rake spec + + - name: Report test coverage + uses: paambaati/codeclimate-action@v3.2.0 + env: + CC_TEST_REPORTER_ID: 997ddf9df5b99897b448d7a7a13e332d57f0e29754d9b9d1414aaee611759422 + with: + coverageLocations: ${{github.workspace}}/coverage/lcov/*.lcov:lcov diff --git a/.rubocop.yml b/.rubocop.yml index 3c1347d..dca866e 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,22 +1,40 @@ AllCops: + NewCops: enable # Output extra information for each offense to make it easier to diagnose: DisplayCopNames: true DisplayStyleGuide: true ExtraDetails: true - # Rubocop enforces rules depending on the oldest version of Ruby which + SuggestExtensions: false + # RuboCop enforces rules depending on the oldest version of Ruby which # your project supports: - TargetRubyVersion: 2.5 + TargetRubyVersion: 3.0 -# The default max line length for this project -Metrics/LineLength: - Max: 100 +Gemspec/DevelopmentDependencies: + EnforcedStyle: gemspec + +# The default max line length is 80 characters +Layout/LineLength: + Max: 120 # The DSL for RSpec and the gemspec file make it very hard to limit block length: Metrics/BlockLength: Exclude: + - "spec/spec_helper.rb" - "spec/**/*_spec.rb" - "*.gemspec" +Metrics/ModuleLength: + CountAsOne: ['hash'] + +# When writing minitest tests, it is very hard to limit test class length: +Metrics/ClassLength: + CountAsOne: ['hash'] + Exclude: + - "test/**/*_test.rb" + +Style/AsciiComments: + Enabled: false + # All Ruby files are required to have a Copyright notice. # Run `rubocop -a` to automatically add missing copyright notices. Style/Copyright: diff --git a/README.md b/README.md index cf6e5d5..3361c8b 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,11 @@ # Publish Documentation to GitHub Pages [![Gem Version](https://badge.fury.io/rb/github_pages_rake_tasks.svg)](https://badge.fury.io/rb/github_pages_rake_tasks) -[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) -[![Build Status](https://travis-ci.com/jcouball/github_pages_rake_tasks.svg?branch=master)](https://travis-ci.com/jcouball/github_pages_rake_tasks) -[![Documentation](https://img.shields.io/badge/Documentation-OK-green.svg)](https://jcouball.github.io/github_pages_rake_tasks/) +[![Documentation](https://img.shields.io/badge/Documentation-Latest-green)](https://rubydoc.info/gems/github_pages_rake_tasks/) +[![Change Log](https://img.shields.io/badge/CHANGELOG-Latest-green)](https://rubydoc.info/gems/github_pages_rake_tasks/file/CHANGELOG.md) +[![Build Status](https://github.com/main-branch/github_pages_rake_tasks/workflows/CI%20Build/badge.svg?branch=main)](https://github.com/main-branch/github_pages_rake_tasks/actions?query=workflow%3ACI%20Build) +[![Maintainability](https://api.codeclimate.com/v1/badges/a67ad0b61d3687e33181/maintainability)](https://codeclimate.com/github/main-branch/github_pages_rake_tasks/maintainability) +[![Test Coverage](https://api.codeclimate.com/v1/badges/a67ad0b61d3687e33181/test_coverage)](https://codeclimate.com/github/main-branch/github_pages_rake_tasks/test_coverage) The `github_pages_rake_tasks` gem creates a rake task that pushes files from a local documentation directory to a remote Git repository branch. @@ -20,15 +22,21 @@ This task is useful for publishing `rdoc` or `yard` documentation using Add this line to your application's Gemfile: - gem 'github_pages_rake_tasks' +```Shell +gem 'github_pages_rake_tasks' +``` And then execute: - $ bundle +```Shell +bundle +``` Or install it directly with the `gem` command line: - $ gem install github_pages_rake_tasks +```Shell +gem install github_pages_rake_tasks +``` ## Usage @@ -50,15 +58,18 @@ GitHubPagesRakeTasks::PublishTask.new do |task| end ``` +An instance of [GithubPagesRakeTasks::State](https://rubydoc.info/gems/github_pages_rake_tasks/GithubPagesRakeTasks/State) +is passed to the initialization block (named `task` in the example above). + See [the full usage documentation](https://github.com/pages/jcouball/guthub_pages_rake_tasks) for more details. ## Development After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment. -To install this gem onto your local machine, run `bundle exec rake install`. -To release a new version, update the version number in `version.rb`, and then run -`bundle exec rake release`, which will create a git tag for the version, push git +To install this gem onto your local machine, run `bundle exec rake install`. +To release a new version, update the version number in `version.rb`, and then 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](https://rubygems.org). ## Contributing diff --git a/Rakefile b/Rakefile index 53b0a25..6f9193c 100644 --- a/Rakefile +++ b/Rakefile @@ -1,41 +1,86 @@ # Copyright (c) 2019 James Couball # frozen_string_literal: true -require 'bundler/gem_tasks' -CLOBBER << 'Gemfile.lock' +desc 'Run the same tasks that the CI build will run' +if RUBY_PLATFORM == 'java' + task default: %w[spec rubocop bundle:audit build] +else + task default: %w[spec rubocop yard yard:audit yard:coverage bundle:audit build] +end -require 'rspec/core/rake_task' -RSpec::Core::RakeTask.new(:spec) -CLEAN << '.rspec_status' -CLEAN << 'coverage' +# Bundler Audit require 'bundler/audit/task' Bundler::Audit::Task.new -require 'bump/tasks' +# Bundler Gem Build + +require 'bundler' +require 'bundler/gem_tasks' + +begin + Bundler.setup(:default, :development) +rescue Bundler::BundlerError => e + warn e.message + warn 'Run `bundle install` to install missing gems' + exit e.status_code +end + +CLEAN << 'pkg' +CLEAN << 'Gemfile.lock' + +# RSpec + +require 'rspec/core/rake_task' + +RSpec::Core::RakeTask.new do + if RUBY_PLATFORM == 'java' + ENV['JAVA_OPTS'] = '-Djdk.io.File.enableADS=true' + ENV['JRUBY_OPTS'] = '--debug' + ENV['NOCOV'] = 'TRUE' + end +end + +CLEAN << 'coverage' +CLEAN << '.rspec_status' +CLEAN << 'rspec-report.xml' + +# Rubocop require 'rubocop/rake_task' + RuboCop::RakeTask.new do |t| - t.options = %w[--format progress --format json --out rubocop-report.json] + t.options = %w[ + --format progress + --format json --out rubocop-report.json + ] end + CLEAN << 'rubocop-report.json' -require 'yard' -YARD::Rake::YardocTask.new -CLEAN << '.yardoc' -CLEAN << 'doc' +unless RUBY_PLATFORM == 'java' + # YARD -require 'yardstick/rake/verify' -require 'yaml' -Yardstick::Rake::Verify.new('yardstick:verify') + require 'yard' + YARD::Rake::YardocTask.new do |t| + t.files = %w[lib/**/*.rb examples/**/*] + end -require 'yardstick/rake/measurement' -Yardstick::Rake::Measurement.new('yardstick:measure') -CLEAN << 'measurements' + CLEAN << '.yardoc' + CLEAN << 'doc' -desc 'Run yardstick to check yard docs' -task :yardstick do - sh "yardstick 'lib/**/*.rb'" -end + # Yardstick + + desc 'Run yardstick to show missing YARD doc elements' + task :'yard:audit' do + sh "yardstick 'lib/**/*.rb'" + end -task default: [:spec, 'bundle:audit', :rubocop, :yard, :build] + # Yardstick coverage + + require 'yardstick/rake/verify' + + Yardstick::Rake::Verify.new(:'yard:coverage') do |verify| + verify.threshold = 100 + end +end diff --git a/github_pages_rake_tasks.gemspec b/github_pages_rake_tasks.gemspec index bbf208f..ed9965b 100644 --- a/github_pages_rake_tasks.gemspec +++ b/github_pages_rake_tasks.gemspec @@ -23,14 +23,14 @@ Gem::Specification.new do |spec| spec.homepage = 'https://github.com/jcouball/github_pages_rake_tasks' spec.license = 'MIT' + spec.required_ruby_version = '>= 3.0.0' - # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host' - # to allow pushing to a single host or delete this section to allow pushing to any host. - if spec.respond_to?(:metadata) - spec.metadata['homepage_uri'] = spec.homepage - spec.metadata['source_code_uri'] = spec.homepage - spec.metadata['changelog_uri'] = spec.homepage - end + spec.metadata['allowed_push_host'] = 'https://rubygems.org' + spec.metadata['rubygems_mfa_required'] = 'true' + + spec.metadata['homepage_uri'] = spec.homepage + spec.metadata['source_code_uri'] = spec.homepage + spec.metadata['changelog_uri'] = spec.homepage # Specify which files should be added to the gem when it is released. # The `git ls-files -z` loads the files in the RubyGem that have been added into git. @@ -41,14 +41,13 @@ Gem::Specification.new do |spec| spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } spec.require_paths = ['lib'] - spec.add_development_dependency 'bump', '~> 0.8' - spec.add_development_dependency 'bundler', '~> 2.0' - spec.add_development_dependency 'bundler-audit', '~> 0.6' - spec.add_development_dependency 'rake', '~> 12.3' - spec.add_development_dependency 'rspec', '~> 3.8' - spec.add_development_dependency 'rubocop', '~> 0.70' - spec.add_development_dependency 'simplecov', '~> 0.16' - spec.add_development_dependency 'simplecov-lcov', '~> 0.7' + spec.add_development_dependency 'bundler-audit', '~> 0.9' + spec.add_development_dependency 'rake', '~> 13.1' + spec.add_development_dependency 'rspec', '~> 3.12' + spec.add_development_dependency 'rubocop', '~> 1.58' + spec.add_development_dependency 'semverify', '0.3.0' + spec.add_development_dependency 'simplecov', '~> 0.22' + spec.add_development_dependency 'simplecov-lcov', '~> 0.8' spec.add_development_dependency 'yard', '~> 0.9' spec.add_development_dependency 'yardstick', '~> 0.9' end diff --git a/lib/github_pages_rake_tasks/interface.rb b/lib/github_pages_rake_tasks/interface.rb index f93748e..278e9c3 100644 --- a/lib/github_pages_rake_tasks/interface.rb +++ b/lib/github_pages_rake_tasks/interface.rb @@ -70,8 +70,5 @@ def initialize # # @return [String] the output of the command # - def `(cmd) - super - end end end diff --git a/lib/github_pages_rake_tasks/publish_task.rb b/lib/github_pages_rake_tasks/publish_task.rb index faea3b2..2357a98 100644 --- a/lib/github_pages_rake_tasks/publish_task.rb +++ b/lib/github_pages_rake_tasks/publish_task.rb @@ -66,6 +66,8 @@ class PublishTask < ::Rake::TaskLib # are passed to the yielded block. # def initialize(*task_args, &initialization_block) + super + @state = State.new # Allow user to override defaults @@ -75,9 +77,7 @@ def initialize(*task_args, &initialization_block) namespace rake_namespace do desc "Publish #{doc_dir} to #{repo_url}##{branch_name}" task :publish do - display_header - publish - display_footer + publish_task end end end @@ -86,15 +86,57 @@ def initialize(*task_args, &initialization_block) # @!visibility private + # Publish the documentation directory to the specified repository and branch + # + # Publishes the document directory to the specified repository and branch + # displaying a header before publishing and a footer after publishing. The header + # and footer are not displayed if the `quiet` flag is set. + # + # @see #display_header + # @see #publish + # @see #display_footer + # + # @return [void] + # + # @api private + # + def publish_task + display_header + publish + display_footer + end + + # Print a header message before the publishing + # + # The message includes the document directory, repository URL, and branch name. + # The message is not printed if the `quiet` flag is set. + # An extra line is printed if the `verbose` flag is set. + # + # @return [void] + # + # @api private def display_header print "Publishing #{doc_dir} to #{repo_url}##{branch_name}..." unless quiet puts if verbose end + # Print a success message after the publishing + # + # The message is not printed if the `quiet` flag is set. + # + # @return [void] + # + # @api private def display_footer puts 'SUCCESS' unless quiet end + # Executes the publishing process + # + # @return [void] + # + # @api private + # def publish interface.verbose(verbose) do interface.mkdir_p(staging_dir) @@ -107,12 +149,24 @@ def publish end end + # Fetch and checks out an existing branch + # + # @return [void] + # + # @api private + # def checkout_existing_branch # only download the needed branch from GitHub interface.sh("git fetch '#{remote_name}' '#{branch_name}'") interface.sh("git checkout '#{branch_name}'") end + # Creates `branch_name` in the remote repository + # + # @return [Boolean] true if the branch exists in the remote repository, false otherwise. + # + # @api private + # def create_new_branch interface.sh("git checkout --orphan '#{branch_name}'") interface.file_write('index.html', 'Future home of documentation') @@ -121,6 +175,12 @@ def create_new_branch interface.sh("git push '#{remote_name}' '#{branch_name}'") end + # Checks if `branch_name` exists in the remote repository + # + # @return [Boolean] true if the branch exists in the remote repository, false otherwise + # + # @api private + # def remote_branch_exists? cmd = "git ls-remote --exit-code --heads '#{repo_url}' '#{branch_name}'" interface.sh(cmd) do |branch_exists, _process_status| @@ -128,15 +188,28 @@ def remote_branch_exists? end end + # Initializes the git repository in `staging_dir` + # + # @return [void] + # + # @api private + # def initialize_git interface.sh('git init') interface.sh("git remote add '#{remote_name}' '#{repo_url}'") end - # Creates `staging_dir` (if needed), clones the remote repository to it, and checks - # out `branch_name`. Creates `branch_name` in the remote is needed. + # Initializes the staging directory + # + # * Creates `staging_dir` (if needed) + # * Clones the remote repository to it + # * Checks out `branch_name` + # * Creates `branch_name` in the remote if needed. + # * Finally, removes all files using git rm # - # Finally, removes all files using git rm + # @return [void] + # + # @api private # def initialize_staging_dir initialize_git @@ -148,28 +221,68 @@ def initialize_staging_dir remove_staging_files end + # Removes the staging directory + # + # @return [void] + # + # @api private + # def clean_staging_dir interface.rm_rf staging_dir end + # @!attribute [r] absolute_doc_dir + # + # The absolute path to `doc_dir` relative to `project_root` + # + # @return [String] + # + # @api private + # def absolute_doc_dir @absolute_doc_dir ||= interface.expand_path(doc_dir, project_root) end + # @!attribute [r] commit_message + # + # The commit message to use when committing the documentation + # + # @return [String] + # + # @api private + # def commit_message @commit_message ||= 'Updating documentation' end + # Removes all files from the staging directory + # + # @return [void] + # + # @api private + # def remove_staging_files interface.sh('git rm -r .') do # ignore failure end end + # Copies the contents of `absolute_doc_dir` to `staging_dir` + # + # @return [void] + # + # @api private + # def copy_doc_dir_to_staging_dir interface.cp_r(File.join(absolute_doc_dir, '.'), staging_dir) end + # Commits and pushes the contents of `staging_dir` to the remote repository + # + # @return [void] + # + # @api private + # def commit_and_push_staging_dir interface.sh('git add .') interface.sh("git commit -m '#{commit_message}'") do |commit_successful, _process_status| diff --git a/lib/github_pages_rake_tasks/state.rb b/lib/github_pages_rake_tasks/state.rb index fd045cc..9190172 100644 --- a/lib/github_pages_rake_tasks/state.rb +++ b/lib/github_pages_rake_tasks/state.rb @@ -17,85 +17,172 @@ module GithubPagesRakeTasks # # Most used attributes are {doc_dir}, {project_root}, {repo_url}, and {branch_name}. # + # @!attribute doc_dir [rw] + # The directory relative to {project_root} containing the documentation + # + # The default value is 'doc'. + # + # @example + # doc_dir = 'doc' + # doc_dir #=> 'doc' + # + # @return [String] + # + # @!attribute project_root [rw] + # The absolute path to the project's root directory + # + # {doc_dir} is relative to this directory. + # + # The default value is the value returned from `git rev-parse --show-toplevel` when + # run in the current working directory. + # + # @example + # project_root = '/home/james/my_project' + # project_root #=> '/home/james/my_project' + # + # @return [String] + # + # @!attribute repo_url [rw] + # The URL to the remote repository to push documentation + # + # The default value is the value returned from `git config --get remote.origin.url` + # + # @example + # repo_url = 'https://github.com/main-branch/my_project.git' + # repo_url #=> 'https://github.com/main-branch/my_project.git' + # + # @return [String] + # + # @!attribute [rw] branch_name + # The branch to push documentation to + # + # The default value is 'gh-pages'. + # + # @example + # branch_name = 'gh-pages' + # branch_name #=> 'gh-pages' + # + # @return [String] + # + # @!attribute [rw] staging_dir + # The directory where the documentation is staged for pushing to the Git remote + # + # All files are copied from {doc_dir} to {staging_dir} where the push to the Git remote is + # done. + # + # @note This directory is deleted at the end of the publish task. + # + # The default value is a temporary directory created with + # [Dir.mktmpdir](https://ruby-doc.org/stdlib-2.6.3/libdoc/tmpdir/rdoc/Dir.html) + # with the prefix 'github-pages-publish-' + # + # @example + # staging_dir = '' + # staging_dir #=> '/home/james/my_project' + # + # @return [String] + # + # @!attribute [rw] quiet + # Silence all output from the `github-pages:publish` task + # + # When {quiet} is true, the `github-pages:publish` task will not emit any output + # unless there is an error. + # + # Setting {quiet} to true will also set {verbose} to false. + # + # The default value is false + # + # @example + # quiet = true + # quiet #=> true + # verbose #=> false + # + # @return [Boolean] + # + # @!attribute verbose + # Make the `github-pages:publish` emit extra output + # + # When {verbose} is true, the `github-pages:publish` task will emit extra output + # that is useful for debugging. + # + # Setting {verbose} to true will also set {quiet} to false. + # + # The default value is false + # + # @example + # verbose = true + # verbose #=> true + # quiet #=> false + # + # @return [Boolean] + # + # @!attribute [rw] rake_namespace + # The Rake namespace for the `publish` task + # + # The default value is 'github-pages' + # + # @example + # rake_namespace = 'my-docs' + # rake_namespace #=> 'my-docs' + # + # @return [String] Rake namespace + # + # @api public + # + # @!attribute [rw] interface + # The object that implements all methods that touch the 'outside' world + # + # An object that implements all methods that touch the world outside of + # the PublishTask class. This includes dealing with the file system, issuing + # shell commands, etc. + # + # The default value is a new instance of {GithubPagesRakeTasks::Interface} + # + # @note {interface} is used for mocking during testing of this gem and is probably + # not useful for users of this gem. + # + # @example + # interface = GithubPagesRakeTasks::Interface.new + # + # @return [GithubPagesRakeTasks::Instance] + # + # @!attribute [rw] remote_name + # The name of the Git remote to use for pushing documentation + # + # The default value is 'origin' + # + # @example + # remote_name = 'my_remote' + # remote_name #=> 'my_remote' + # + # @return [String] the Git remote name + # class State - # The directory, relative to {project_root}, that contains the documentation to - # publish to GitHub. - # - # The default value is 'doc' - # - # @return [String] directory - # def doc_dir @doc_dir ||= 'doc' end - attr_writer :doc_dir - - # The absolute path to the project's Git repository. {doc_dir} is relative to this - # path. - # - # The default value is the value returned from `git rev-parse --show-toplevel` when - # run in the current working directory - # - # @return [String] directory - # + + attr_writer :doc_dir, :project_root, :repo_url, :branch_name, :staging_dir, :remote_name, + :interface, :rake_namespace + def project_root @project_root ||= interface.send(:`, 'git rev-parse --show-toplevel').chomp end - attr_writer :project_root - - # The URL to the remote repository to push documentation. - # - # The default value is the value returned from `git config --get remote.origin.url` - # - # @return [String] url - # + def repo_url @repo_url ||= Dir.chdir(project_root) do |_path| interface.send(:`, "git config --get remote.#{remote_name}.url").chomp end end - attr_writer :repo_url - - # The branch to push documentation to. - # - # The default value is 'gh-pages' - # - # @return [String] the branch name - # + def branch_name @branch_name ||= 'gh-pages' end - attr_writer :branch_name - - # The directory where the documentation is staged prior to pushing to the Git remote. - # All files are copied from {doc_dir} to {staging_dir} where the push to the Git remote is - # done. - # - # @note This directory is deleted at the end of the publish task. - # - # The default value is a temporary directory created with - # [Dir.mktmpdir](https://ruby-doc.org/stdlib-2.6.3/libdoc/tmpdir/rdoc/Dir.html) - # with the prefix 'github-pages-publish-' - # - # @return [String] a temporary directory. - # + def staging_dir @staging_dir ||= interface.mktmpdir('github-pages-publish-') end - attr_writer :staging_dir - - # @!attribute quiet - # Silence all output from the `github-pages:publish` task. - # - # When {quiet} is true, the `github-pages:publish` task will not emit any output - # unless there is an error. - # - # Setting {quiet} to true will also set {verbose} to false. - # - # The default value is false - # - # @return [Boolean] the quiet flag value - # + def quiet return @quiet if instance_variable_defined?(:@quiet) @@ -107,18 +194,6 @@ def quiet=(value) @verbose = false if quiet end - # @!attribute verbose - # Make the `github-pages:publish` emit extra output. - # - # When {verbose} is true, the `github-pages:publish` task will emit extra output - # that is useful for debugging. - # - # Setting {verbose} to true will also set {quiet} to false. - # - # The default value is false - # - # @return [Boolean] the verbose flag value - # def verbose return @verbose if instance_variable_defined?(:@verbose) @@ -130,42 +205,16 @@ def verbose=(value) @quiet = false if verbose end - # The name of the Git remote to use for pushing documentation. - # - # The default value is 'origin' - # - # @return [String] the Git remote name - # def remote_name @remote_name ||= 'origin' end - attr_writer :remote_name - - # An object that implements all methods that touch the world outside of - # the PublishTask class. This includes dealing with the file system, issuing - # shell commands, etc. - # - # The default value is a new instance of {GithubPagesRakeTasks::Interface} - # - # @note {interface} is used for mocking during testing of this gem and is probably - # not useful for users of this gem. - # - # @return [GithubPagesRakeTasks::Instance] an interface object - # + def interface @interface ||= Interface.new end - attr_writer :interface - - # The Rake namespace for the publish task. - # - # The default value is 'github-pages' - # - # @return [String] Rake namespace - # + def rake_namespace @rake_namespace ||= 'github-pages' end - attr_writer :rake_namespace end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index a1ec627..d1a68b4 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,9 +1,9 @@ # Copyright (c) 2019 James Couball # frozen_string_literal: true -require 'bundler/setup' -require 'simplecov' -require 'simplecov-lcov' +require 'rspec' + +# Configure RSpec RSpec.configure do |config| # Enable flags like --only-failures and --next-failure @@ -18,11 +18,51 @@ end # Setup simplecov -SimpleCov::Formatter::LcovFormatter.config.report_with_single_file = true -SimpleCov.formatters = [ - SimpleCov::Formatter::LcovFormatter, - SimpleCov::Formatter::HTMLFormatter -] + +require 'simplecov' +require 'simplecov-lcov' + +SimpleCov.formatters = [SimpleCov::Formatter::HTMLFormatter, SimpleCov::Formatter::LcovFormatter] + +# Fail the rspec run if code coverage falls below the configured threshold +# +# Skip this check if the NOCOV environment variable is set to TRUE +# +# ```Shell +# NOCOV=TRUE rspec +# ``` +# +# Example of running the tests in an infinite loop writing failures to `fail.txt`: +# +# ```Shell +# while true; do +# NOCOV=TRUE rspec spec/process_executer/monitored_pipe_spec.rb | sed -n '/^Failures:$/, /^Finished /p' >> fail.txt +# done +# ```` +# +test_coverage_threshold = 100 +SimpleCov.at_exit do + unless RSpec.configuration.dry_run? + SimpleCov.result.format! + + if ENV['NOCOV']&.upcase != 'TRUE' && SimpleCov.result.covered_percent < test_coverage_threshold + warn "FAIL: RSpec Test coverage fell below #{test_coverage_threshold}%" + + warn "\nThe following lines were not covered by tests:\n" + SimpleCov.result.files.each do |source_file| # SimpleCov::SourceFile + source_file.missed_lines.each do |line| # SimpleCov::SourceFile::Line + puts " #{source_file.project_filename}:#{line.number}" + end + end + warn "\n" + + exit 1 + end + end +end + +# Start SimpleCov before loading the code to be tested + SimpleCov.start require 'github_pages_rake_tasks'