├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── pull_request_template.md └── workflows │ └── ci.yml ├── .gitignore ├── .reek.yml ├── .rspec ├── .rubocop.yml ├── .ruby-version ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Gemfile ├── LICENSE.txt ├── README.md ├── Rakefile ├── bin ├── console └── setup ├── exe └── rsgem ├── lib ├── rsgem.rb └── rsgem │ ├── ci_providers │ ├── base.rb │ ├── github_actions.rb │ └── travis.rb │ ├── cli │ ├── commands.rb │ └── commands │ │ ├── new.rb │ │ └── version.rb │ ├── colors.rb │ ├── constants.rb │ ├── context.rb │ ├── dependencies │ ├── base.rb │ ├── rake.rb │ ├── reek.rb │ ├── rspec.rb │ ├── rubocop.rb │ └── simplecov.rb │ ├── errors │ ├── base.rb │ └── missing_gem_name.rb │ ├── gem.rb │ ├── support │ ├── Rakefile │ ├── github_actions.yml │ ├── reek.yml │ ├── rubocop.yml │ └── travis.yml │ ├── tasks │ ├── add_ci_provider.rb │ ├── add_code_analysis.rb │ ├── add_dependency.rb │ ├── base.rb │ ├── bundle_dependencies.rb │ ├── clean_gemfile.rb │ ├── clean_gemspec.rb │ ├── create_gem.rb │ ├── ensure_author.rb │ ├── ignore_gemfile_lock.rb │ ├── output.rb │ ├── run_rubocop.rb │ ├── set_bundled_files.rb │ ├── set_license_file.rb │ ├── set_required_ruby_version.rb │ └── simple_cov_post_install.rb │ └── version.rb ├── rsgem.gemspec └── spec ├── cli_spec.rb ├── gem_spec.rb ├── rsgem_spec.rb └── spec_helper.rb /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "[Bug] " 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## Bug report: 11 | * **Expected Behavior**: 12 | * **Actual Behavior**: 13 | * **Steps to Reproduce**: 14 | 1. 15 | 2. 16 | 3. 17 | 18 | * **Version of the repo**: 19 | * **Ruby and Rails Version**: 20 | * **Rails Stacktrace**: this can be found in the `log/development.log` or `log/test.log`, if this is applicable. 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '[FEATURE]' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 11 | 12 | **Is your feature request related to a problem? Please describe.** 13 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 14 | 15 | **Describe the solution you'd like** 16 | A clear and concise description of what you want to happen. 17 | 18 | **Describe alternatives you've considered** 19 | A clear and concise description of any alternative solutions or features you've considered. 20 | 21 | **Additional context** 22 | Add any other context or screenshots about the feature request here. 23 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ### Summary 2 | 3 | 7 | 8 | ### Other Information 9 | 10 | 13 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | strategy: 9 | matrix: 10 | ruby_version: [2.5.x, 2.6.x, 2.7.x] 11 | 12 | steps: 13 | - uses: actions/checkout@v2 14 | - name: Set up Ruby 15 | uses: actions/setup-ruby@v1 16 | with: 17 | ruby-version: ${{ matrix.ruby_version }} 18 | - name: Calculate variable dynamic values 19 | id: dynamic_values 20 | run: | 21 | echo "::set-output name=installed_ruby_version::$(ruby -e 'print RUBY_VERSION')" 22 | echo "::set-output name=cacheTimeAnchor::$(ruby -e 'require %Q{date}; cacheExpirationSeconds = 60*60*24; print (Time.now.to_i / cacheExpirationSeconds)')" 23 | - name: Download CodeClimate reporter 24 | run: | 25 | curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter 26 | chmod +x ./cc-test-reporter 27 | ./cc-test-reporter before-build 28 | env: 29 | CC_TEST_REPORTER_ID: 0caaf0c68cbf5597ac7eb5207944c878a0b3fba46dbfa113093b861f2d25762e 30 | - name: Install and config bundler 31 | run: | 32 | gem install bundler:2.1.4 33 | - name: Generate 'Gemfile.lock' before caching gems 34 | run: | 35 | bundle lock --update 36 | - uses: actions/cache@v2 37 | with: 38 | path: vendor/bundle 39 | key: ${{ runner.os }}-ruby_v${{ steps.dynamic_values.outputs.installed_ruby_version }}-time_${{steps.dynamic_values.outputs.cacheTimeAnchor}}-gems-${{ hashFiles('**/Gemfile.lock') }} 40 | - name: Install dependencies 41 | run: | 42 | bundle config set path 'vendor/bundle' 43 | bundle install --jobs 4 --retry 3 44 | - name: Run code analysis 45 | run: | 46 | bundle exec rake code_analysis 47 | - name: Run tests 48 | run: | 49 | bundle exec rspec 50 | - name: Report to CodeClimate 51 | run: | 52 | ./cc-test-reporter after-build --exit-code 0 53 | env: 54 | CC_TEST_REPORTER_ID: 0caaf0c68cbf5597ac7eb5207944c878a0b3fba46dbfa113093b861f2d25762e 55 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle/ 2 | /.yardoc 3 | /_yardoc/ 4 | /coverage/ 5 | /doc/ 6 | /pkg/ 7 | /spec/reports/ 8 | /tmp/ 9 | 10 | # rspec failure tracking 11 | .rspec_status 12 | 13 | Gemfile.lock 14 | -------------------------------------------------------------------------------- /.reek.yml: -------------------------------------------------------------------------------- 1 | detectors: 2 | Attribute: 3 | enabled: false 4 | exclude: [] 5 | BooleanParameter: 6 | enabled: true 7 | exclude: [] 8 | ClassVariable: 9 | enabled: false 10 | exclude: [] 11 | ControlParameter: 12 | enabled: true 13 | exclude: [] 14 | DataClump: 15 | enabled: true 16 | exclude: [] 17 | max_copies: 2 18 | min_clump_size: 2 19 | DuplicateMethodCall: 20 | enabled: true 21 | exclude: [] 22 | max_calls: 1 23 | allow_calls: [] 24 | FeatureEnvy: 25 | enabled: true 26 | exclude: [] 27 | InstanceVariableAssumption: 28 | enabled: false 29 | IrresponsibleModule: 30 | enabled: false 31 | exclude: [] 32 | LongParameterList: 33 | enabled: true 34 | exclude: [] 35 | max_params: 4 36 | overrides: 37 | initialize: 38 | max_params: 5 39 | LongYieldList: 40 | enabled: true 41 | exclude: [] 42 | max_params: 3 43 | ManualDispatch: 44 | enabled: true 45 | exclude: [] 46 | MissingSafeMethod: 47 | enabled: false 48 | exclude: [] 49 | ModuleInitialize: 50 | enabled: true 51 | exclude: [] 52 | NestedIterators: 53 | enabled: true 54 | exclude: [] 55 | max_allowed_nesting: 2 56 | ignore_iterators: [] 57 | NilCheck: 58 | enabled: false 59 | exclude: [] 60 | RepeatedConditional: 61 | enabled: true 62 | exclude: [] 63 | max_ifs: 3 64 | SubclassedFromCoreClass: 65 | enabled: true 66 | exclude: [] 67 | TooManyConstants: 68 | enabled: true 69 | exclude: [] 70 | max_constants: 5 71 | TooManyInstanceVariables: 72 | enabled: true 73 | exclude: [] 74 | max_instance_variables: 9 75 | TooManyMethods: 76 | enabled: true 77 | exclude: [] 78 | max_methods: 25 79 | TooManyStatements: 80 | enabled: true 81 | exclude: 82 | - initialize 83 | max_statements: 12 84 | UncommunicativeMethodName: 85 | enabled: true 86 | exclude: [] 87 | reject: 88 | - "/^[a-z]$/" 89 | - "/[0-9]$/" 90 | - "/[A-Z]/" 91 | accept: [] 92 | UncommunicativeModuleName: 93 | enabled: true 94 | exclude: [] 95 | reject: 96 | - "/^.$/" 97 | - "/[0-9]$/" 98 | accept: 99 | - Inline::C 100 | - "/V[0-9]/" 101 | UncommunicativeParameterName: 102 | enabled: true 103 | exclude: [] 104 | reject: 105 | - "/^.$/" 106 | - "/[0-9]$/" 107 | - "/[A-Z]/" 108 | accept: [] 109 | UncommunicativeVariableName: 110 | enabled: true 111 | exclude: [] 112 | reject: 113 | - "/^.$/" 114 | - "/[0-9]$/" 115 | - "/[A-Z]/" 116 | accept: 117 | - _ 118 | UnusedParameters: 119 | enabled: true 120 | exclude: [] 121 | UnusedPrivateMethod: 122 | enabled: false 123 | UtilityFunction: 124 | enabled: false 125 | 126 | exclude_paths: 127 | - config 128 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --format documentation 2 | --color 3 | --require spec_helper 4 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | inherit_gem: 2 | rubocop-rootstrap: 3 | - config/default_edge.yml 4 | 5 | Gemspec/RequiredRubyVersion: 6 | Enabled: false 7 | 8 | Metrics/BlockLength: 9 | Exclude: 10 | - spec/**/* 11 | 12 | Metrics/AbcSize: 13 | Exclude: 14 | - lib/rsgem/gem.rb 15 | - lib/rsgem/tasks/output.rb 16 | -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | 2.7.1 2 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # master 2 | 3 | # 1.1.0 4 | 5 | * Update Bundler and Ruby versions 6 | 7 | *Horacio Bertorello* 8 | 9 | # 1.0.0 10 | 11 | * Using Github Actions as the default CI provider 12 | * Dropping Ruby 2.4 support 13 | * New CLI output format 14 | * Add rubocop-rootstrap 15 | 16 | *Juan Manuel Ramallo* 17 | 18 | * Add placeholder `email` and `name` when no git user is set, issuing a warning while doing so. 19 | * Add colors to output during gem creation. 20 | 21 | *Jake Yesbeck* 22 | 23 | # 0.4.0 24 | 25 | * Cache bundler directory for 24hs in Github Actions 26 | 27 | *Martín Rubí* 28 | 29 | # 0.3.0 30 | 31 | * Add task to replace system user name in LICENSE.txt with Rootstrap name 32 | 33 | *Juan Francisco Ferrari* 34 | 35 | # 0.2.0 36 | 37 | * Add default configuration for simplecov in spec_helper.rb 38 | 39 | * Display an error message if bundler failed to run or is not present in the system 40 | 41 | *Juan Manuel Ramallo* 42 | 43 | # 0.1.3 44 | 45 | * Fix gem creation task on Linux 46 | 47 | *Juan Aparicio* 48 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at ramallojuanm@gmail.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at [https://contributor-covenant.org/version/1/4][version] 72 | 73 | [homepage]: https://contributor-covenant.org 74 | [version]: https://contributor-covenant.org/version/1/4/ 75 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Contributing ## 2 | 3 | You can contribute to this repo if you have an issue, found a bug or think there's some functionality required that would add value to the gem. To do so, please check if there's not already an [issue](https://github.com/rootstrap/rsgem/issues) for that, if you find there's not, create a new one with as much detail as possible. 4 | 5 | If you want to contribute with code as well, please follow the next steps: 6 | 7 | 1. Read, understand and agree to our [code of conduct](https://github.com/rootstrap/rsgem/blob/master/CODE_OF_CONDUCT.md) 8 | 2. [Fork the repo](https://help.github.com/articles/about-forks/) 9 | 3. Clone the project into your machine: 10 | `$ git clone git@github.com:rootstrap/rsgem.git` 11 | 4. Access the repo: 12 | `$ cd rsgem` 13 | 5. Create your feature/bugfix branch: 14 | `$ git checkout -b your_new_feature` 15 | or 16 | `$ git checkout -b fix/your_fix` in case of a bug fix 17 | (if your PR is to address an existing issue, it would be good to name the branch after the issue, for example: if you are trying to solve issue 182, then a good idea for the branch name would be `182_your_new_feature`) 18 | 6. Write tests for your changes (feature/bug) 19 | 7. Code your (feature/bugfix) 20 | 8. Run the code analysis tool by doing: 21 | `$ rake code_analysis` 22 | 9. Run the tests: 23 | `$ bundle exec rspec` 24 | All tests must pass. If all tests (both code analysis and rspec) do pass, then you are ready to go to the next step: 25 | 10. Commit your changes: 26 | `$ git commit -m 'Your feature or bugfix title'` 27 | 11. Push to the branch `$ git push origin your_new_feature` 28 | 12. Create a new [pull request](https://help.github.com/articles/creating-a-pull-request/) 29 | 30 | Some helpful guides that will help you know how we work: 31 | 1. [Code review](https://github.com/rootstrap/tech-guides/tree/master/code-review) 32 | 2. [GIT workflow](https://github.com/rootstrap/tech-guides/tree/master/git) 33 | 3. [Ruby style guide](https://github.com/rootstrap/tech-guides/tree/master/ruby) 34 | 4. [Rails style guide](https://github.com/rootstrap/tech-guides/blob/master/ruby/rails.md) 35 | 5. [RSpec style guide](https://github.com/rootstrap/tech-guides/blob/master/ruby/rspec/README.md) 36 | 37 | For more information or guides like the ones mentioned above, please check our [tech guides](https://github.com/rootstrap/tech-guides). Keep in mind that the more you know about these guides, the easier it will be for your code to get approved and merged. 38 | 39 | Note: You can push as many commits as you want when working on a pull request, we just ask that they are descriptive and tell a story. Try to open a pull request with just one commit but if you think you need to divide what you did into more commits to convey what you are trying to do go for it. 40 | 41 | Thank you very much for your time and for considering helping in this project. 42 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source 'https://rubygems.org' 4 | 5 | gemspec 6 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2020 Rootstrap 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # `rsgem` - Rootstrap's gem generator 2 | 3 | [![Gem Version](https://badge.fury.io/rb/rsgem.svg)](https://badge.fury.io/rb/rsgem) 4 | [![ci](https://github.com/rootstrap/rsgem/workflows/ci/badge.svg)](https://github.com/rootstrap/rsgem/actions?query=workflow%3Aci) 5 | [![Maintainability](https://api.codeclimate.com/v1/badges/d956ef2db5e9d02db891/maintainability)](https://codeclimate.com/github/rootstrap/rsgem/maintainability) 6 | [![Test Coverage](https://api.codeclimate.com/v1/badges/d956ef2db5e9d02db891/test_coverage)](https://codeclimate.com/github/rootstrap/rsgem/test_coverage) 7 | 8 | `rsgem` is a tool to help you start developing gems with the defaults we use at Rootstrap. 9 | 10 | ## Installation 11 | 12 | $ gem install rsgem 13 | 14 | We highly suggest to not include `rsgem` in your Gemfile. 15 | `rsgem` is not a library, and should not affect the dependency tree of your project. 16 | 17 | ## Usage 18 | 19 | ``` 20 | rsgem new NAME 21 | ``` 22 | 23 | RSGem will solve the following tasks for you: 24 | 25 | 1. Create a folder for your gem leveraging bundler's defaults. (You need bundler in your system) 26 | 1. Add the following dependencies: 27 | - [Rake](https://github.com/ruby/rake) 28 | - [Reek](https://github.com/troessner/reek) 29 | - [RSpec](https://github.com/rspec/rspec) 30 | - [Rubocop](https://github.com/rubocop-hq/rubocop) 31 | - [Simplecov](https://github.com/colszowka/simplecov) 32 | - We use Simplecov at 0.17.1 because that's the latest compatible version with CodeClimate. 33 | 1. Add configuration files for Reek and Rubocop with default Rootstrap's configuration. 34 | 1. Add a rake task to run Rubocop and Reek by calling `rake code_analysis`. 35 | 1. [Clean the Gemfile](https://github.com/rootstrap/tech-guides/blob/master/open-source/developing_gems.md#gemfilegemfilelockgemspec). 36 | 1. [Git ignore the Gemfile.lock](https://github.com/rootstrap/tech-guides/blob/master/open-source/developing_gems.md#gemfilegemfilelockgemspec) 37 | 1. Add a CI provider configuration. GitHub Actions and Travis are available providers. Travis is the default. 38 | 1. Set the bundled files to be a short list of files. By default the gem will bundle: 39 | - LICENSE.txt 40 | - README.md 41 | - lib/**/* (everything inside lib) 42 | 1. Apply Rubocop style fixes 43 | 44 | #### Examples 45 | 46 | ``` 47 | rsgem new foo 48 | ``` 49 | Creates a new gem called foo. 50 | 51 | ``` 52 | rsgem new bar --ci=github_actions 53 | ``` 54 | Creates a new gem called bar that uses Github Actions as the CI provider. 55 | 56 | ``` 57 | rsgem new foo_bar --bundler=--ext 58 | ``` 59 | Creates a new gem called foo_bar and passes the [--ext flag to bundler](https://bundler.io/v2.0/man/bundle-gem.1.html#OPTIONS). 60 | 61 | ``` 62 | rsgem new bar_foo --bundler='--several --flags' 63 | ``` 64 | Creates a new gem passing several flags to bundler. 65 | 66 | #### Help 67 | 68 | ``` 69 | rsgem -h 70 | ``` 71 | Displays global help. 72 | 73 | ``` 74 | rsgem new -h 75 | ``` 76 | Displays help for the `new` command. 77 | 78 | ## Development 79 | 80 | After checking out the repo, run `bin/setup` to install dependencies. Then, run `bundle exec rspec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment. 81 | 82 | 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). 83 | 84 | ## Contributing 85 | 86 | Bug reports and pull requests are welcome on GitHub at https://github.com/rootstrap/rsgem. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/rootstrap/rsgem/blob/master/CODE_OF_CONDUCT.md). 87 | 88 | 89 | ## License 90 | 91 | The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT). 92 | 93 | ## Code of Conduct 94 | 95 | Everyone interacting in the Rootstrap project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/rootstrap/rsgem/blob/master/CODE_OF_CONDUCT.md). 96 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | task :code_analysis do 4 | sh 'bundle exec rubocop lib spec' 5 | end 6 | -------------------------------------------------------------------------------- /bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | require 'bundler/setup' 5 | require 'rsgem' 6 | 7 | # You can add fixtures and/or initialization code here to make experimenting 8 | # with your gem easier. You can also use a different console, if you like. 9 | 10 | # (If you use this, don't forget to add pry to your Gemfile!) 11 | # require "pry" 12 | # Pry.start 13 | 14 | require 'irb' 15 | IRB.start(__FILE__) 16 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | set -vx 5 | 6 | bundle install 7 | 8 | # Do any other automated setup that you need to do here 9 | -------------------------------------------------------------------------------- /exe/rsgem: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | require 'bundler/setup' 5 | require 'rsgem' 6 | 7 | Dry::CLI.new(RSGem::CLI::Commands).call 8 | -------------------------------------------------------------------------------- /lib/rsgem.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'date' 4 | require 'fileutils' 5 | require 'rsgem/version' 6 | require 'rsgem/gem' 7 | require 'rsgem/errors/base' 8 | require 'rsgem/errors/missing_gem_name' 9 | require 'rsgem/ci_providers/base' 10 | require 'rsgem/ci_providers/github_actions' 11 | require 'rsgem/ci_providers/travis' 12 | require 'rsgem/tasks/output' 13 | require 'rsgem/tasks/base' 14 | require 'rsgem/tasks/add_ci_provider' 15 | require 'rsgem/tasks/add_code_analysis' 16 | require 'rsgem/tasks/add_dependency' 17 | require 'rsgem/tasks/clean_gemfile' 18 | require 'rsgem/tasks/create_gem' 19 | require 'rsgem/tasks/ensure_author' 20 | require 'rsgem/tasks/ignore_gemfile_lock' 21 | require 'rsgem/tasks/clean_gemspec' 22 | require 'rsgem/tasks/bundle_dependencies' 23 | require 'rsgem/tasks/run_rubocop' 24 | require 'rsgem/tasks/set_bundled_files' 25 | require 'rsgem/tasks/set_license_file' 26 | require 'rsgem/tasks/simple_cov_post_install' 27 | require 'rsgem/tasks/set_required_ruby_version' 28 | require 'rsgem/dependencies/base' 29 | require 'rsgem/dependencies/rake' 30 | require 'rsgem/dependencies/reek' 31 | require 'rsgem/dependencies/rspec' 32 | require 'rsgem/dependencies/rubocop' 33 | require 'rsgem/dependencies/simplecov' 34 | require 'rsgem/constants' 35 | require 'rsgem/colors' 36 | require 'rsgem/context' 37 | require 'dry/cli' 38 | require 'rsgem/cli/commands/new' 39 | require 'rsgem/cli/commands/version' 40 | require 'rsgem/cli/commands' 41 | 42 | module RSGem 43 | end 44 | -------------------------------------------------------------------------------- /lib/rsgem/ci_providers/base.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module RSGem 4 | module CIProviders 5 | class Base 6 | attr_reader :config_file_destination, :config_file_source, :name, :display_name 7 | 8 | def initialize(display_name:, name:, config_file_source: nil, config_file_destination: nil) 9 | @config_file_source = config_file_source 10 | @config_file_destination = config_file_destination 11 | @display_name = display_name 12 | @name = name 13 | end 14 | 15 | def install(context) 16 | remove_travis(context) 17 | destination = "#{context.folder_path}/#{config_file_destination}" 18 | 19 | ::File.delete(destination) if ::File.exist?(destination) 20 | ::FileUtils.mkdir_p(::File.dirname(destination)) 21 | ::File.open(destination, 'w') do |file| 22 | file.puts config_file_source_content 23 | end 24 | end 25 | 26 | private 27 | 28 | def config_file_source_content 29 | ::File.read(config_file_source) 30 | end 31 | 32 | # 33 | # `bundle gem` adds travis by default 34 | # 35 | def remove_travis(context) 36 | travis_path = "#{context.folder_path}/.travis.yml" 37 | return unless ::File.exist?(travis_path) 38 | 39 | ::File.delete(travis_path) 40 | end 41 | end 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /lib/rsgem/ci_providers/github_actions.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module RSGem 4 | module CIProviders 5 | GithubActions = Base.new( 6 | config_file_source: "#{File.dirname(__FILE__)}/../support/github_actions.yml", 7 | config_file_destination: '.github/workflows/ci.yml', 8 | display_name: 'Github Actions', 9 | name: 'github_actions' 10 | ) 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /lib/rsgem/ci_providers/travis.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module RSGem 4 | module CIProviders 5 | Travis = Base.new(config_file_source: "#{File.dirname(__FILE__)}/../support/travis.yml", 6 | config_file_destination: '.travis.yml', 7 | display_name: 'Travis', 8 | name: 'travis') 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /lib/rsgem/cli/commands.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module RSGem 4 | module CLI 5 | module Commands 6 | extend Dry::CLI::Registry 7 | 8 | register 'new', New 9 | register 'version', Version, aliases: ['v', '-v', '--version'] 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /lib/rsgem/cli/commands/new.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module RSGem 4 | module CLI 5 | module Commands 6 | class New < Dry::CLI::Command 7 | desc 'Create a new gem' 8 | 9 | argument :gem_name, type: :string, required: true, desc: 'Name of your new gem' 10 | option :bundler, type: :string, 11 | default: nil, 12 | desc: 'Bundler options to use' 13 | option :ci, type: :string, 14 | default: RSGem::Constants::DEFAULT_CI_PROVIDER.name, 15 | values: RSGem::Constants::CI_PROVIDERS.map(&:name), 16 | desc: 'CI provider to use' 17 | 18 | example [ 19 | 'foo # Creates a new gem called foo', 20 | 'bar --ci=travis # Creates a new gem called bar, with Travis as the '\ 21 | 'CI provider', 22 | 'foo_bar --bundler=--ext # Creates a new gem called foo_bar passing the --ext flag to '\ 23 | 'bundler' 24 | ] 25 | 26 | def call(**options) 27 | RSGem::Gem.new(gem_name: options[:gem_name], 28 | ci_provider: options[:ci], 29 | bundler_options: options[:bundler]).create 30 | end 31 | end 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /lib/rsgem/cli/commands/version.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module RSGem 4 | module CLI 5 | module Commands 6 | class Version < Dry::CLI::Command 7 | desc 'Print version' 8 | 9 | def call(*) 10 | puts RSGem::VERSION 11 | end 12 | end 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /lib/rsgem/colors.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module RSGem 4 | class Colors 5 | MAPPING = { 6 | default: 39, 7 | red: 31, 8 | green: 32, 9 | yellow: 33, 10 | blue: 34, 11 | white: 97 12 | }.freeze 13 | 14 | class << self 15 | def colorize(string, color) 16 | "\e[#{color_code(color)}m#{string}\e[0m" 17 | end 18 | 19 | def color_code(color) 20 | MAPPING[color] || MAPPING[:default] 21 | end 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /lib/rsgem/constants.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module RSGem 4 | module Constants 5 | CI_PROVIDERS = [ 6 | RSGem::CIProviders::GithubActions, 7 | RSGem::CIProviders::Travis 8 | ].freeze 9 | DEFAULT_CI_PROVIDER = RSGem::CIProviders::GithubActions 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /lib/rsgem/context.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module RSGem 4 | class Context 5 | attr_reader :options 6 | 7 | def initialize(options:) 8 | @options = options 9 | 10 | raise Errors::MissingGemName unless options[:gem_name] 11 | end 12 | 13 | def bundler_options 14 | @bundler_options ||= options[:bundler_options] 15 | end 16 | 17 | def ci_provider 18 | @ci_provider ||= begin 19 | if (name = options[:ci_provider]) 20 | RSGem::Constants::CI_PROVIDERS.detect do |provider| 21 | provider.name == name 22 | end 23 | else 24 | RSGem::Constants::DEFAULT_CI_PROVIDER 25 | end 26 | end 27 | end 28 | 29 | def gemfile_path 30 | "#{folder_path}/Gemfile" 31 | end 32 | 33 | def gem_name 34 | @gem_name ||= options[:gem_name] 35 | end 36 | 37 | def gemspec_path 38 | "#{folder_path}/#{gem_name}.gemspec" 39 | end 40 | 41 | def license_path 42 | "#{folder_path}/LICENSE.txt" 43 | end 44 | 45 | def folder_path 46 | `pwd`.sub("\n", '/') + gem_name 47 | end 48 | 49 | def gitignore_path 50 | "#{folder_path}/.gitignore" 51 | end 52 | 53 | def rakefile_path 54 | "#{folder_path}/Rakefile" 55 | end 56 | 57 | def spec_helper_path 58 | "#{folder_path}/spec/spec_helper.rb" 59 | end 60 | end 61 | end 62 | -------------------------------------------------------------------------------- /lib/rsgem/dependencies/base.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module RSGem 4 | module Dependencies 5 | class Base 6 | attr_reader :config_file_destination, :config_file_source, :mode, :name, :post_install_task, 7 | :version 8 | 9 | def initialize(name:, **args) 10 | @config_file_source = args[:config_file_source] 11 | @config_file_destination = args[:config_file_destination] 12 | @mode = args[:mode] || 'development' # Either `development' or `runtime' 13 | @name = name 14 | @post_install_task = args[:post_install_task] 15 | version = args[:version] 16 | @version = version ? "'#{version}'" : nil 17 | end 18 | 19 | def install(context) 20 | if config_file_source 21 | File.open("#{context.folder_path}/#{config_file_destination}", 'w') do |file| 22 | file.puts config_file_source_content 23 | end 24 | end 25 | 26 | post_install_task&.new(context: context)&.perform 27 | end 28 | 29 | private 30 | 31 | def config_file_source_content 32 | File.read(config_file_source) 33 | end 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /lib/rsgem/dependencies/rake.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module RSGem 4 | module Dependencies 5 | Rake = Base.new(name: 'rake') 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /lib/rsgem/dependencies/reek.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module RSGem 4 | module Dependencies 5 | Reek = Base.new( 6 | config_file_source: "#{File.dirname(__FILE__)}/../support/reek.yml", 7 | config_file_destination: '.reek.yml', 8 | name: 'reek' 9 | ) 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /lib/rsgem/dependencies/rspec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module RSGem 4 | module Dependencies 5 | RSpec = Base.new(name: 'rspec') 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /lib/rsgem/dependencies/rubocop.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module RSGem 4 | module Dependencies 5 | Rubocop = Base.new( 6 | config_file_source: "#{File.dirname(__FILE__)}/../support/rubocop.yml", 7 | config_file_destination: '.rubocop.yml', 8 | name: 'rubocop-rootstrap' 9 | ) 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /lib/rsgem/dependencies/simplecov.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module RSGem 4 | module Dependencies 5 | # This is the latest stable version working correctly with codeclimate 6 | # 0.18+ does not work currently https://github.com/codeclimate/test-reporter/issues/413 7 | Simplecov = Base.new(name: 'simplecov', version: '~> 0.17.1', 8 | post_install_task: RSGem::Tasks::SimpleCovPostInstall) 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /lib/rsgem/errors/base.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module RSGem 4 | module Errors 5 | class Base < StandardError 6 | end 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /lib/rsgem/errors/missing_gem_name.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module RSGem 4 | module Errors 5 | class MissingGemName < Base 6 | end 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /lib/rsgem/gem.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module RSGem 4 | class Gem 5 | attr_reader :options 6 | 7 | def initialize(options) 8 | @options = options 9 | end 10 | 11 | def create 12 | puts 'Creating gem...' 13 | create_gem 14 | ensure_author 15 | add_code_analysis 16 | add_dependencies 17 | clean_gemfile 18 | ignore_gemfile_lock 19 | add_ci_provider 20 | clean_gemspec 21 | set_bundled_files 22 | set_license_file 23 | set_required_ruby_version 24 | bundle_dependencies 25 | run_rubocop 26 | puts "#{context.gem_name} created" 27 | end 28 | 29 | private 30 | 31 | def add_ci_provider 32 | Tasks::AddCIProvider.new(context: context).call 33 | end 34 | 35 | def add_code_analysis 36 | Tasks::AddCodeAnalysis.new(context: context).call 37 | end 38 | 39 | def add_dependencies 40 | [ 41 | Dependencies::Rake, 42 | Dependencies::Reek, 43 | Dependencies::RSpec, 44 | Dependencies::Rubocop, 45 | Dependencies::Simplecov 46 | ].each do |dependency| 47 | Tasks::AddDependency.new(context: context, dependency: dependency).call 48 | end 49 | end 50 | 51 | def clean_gemfile 52 | Tasks::CleanGemfile.new(context: context).call 53 | end 54 | 55 | def clean_gemspec 56 | Tasks::CleanGemspec.new(context: context).call 57 | end 58 | 59 | def context 60 | @context ||= Context.new(options: options) 61 | end 62 | 63 | def create_gem 64 | Tasks::CreateGem.new(context: context).call 65 | end 66 | 67 | def ensure_author 68 | Tasks::EnsureAuthor.new(context: context).call 69 | end 70 | 71 | def ignore_gemfile_lock 72 | Tasks::IgnoreGemfileLock.new(context: context).call 73 | end 74 | 75 | def run_rubocop 76 | Tasks::RunRubocop.new(context: context).call 77 | end 78 | 79 | def bundle_dependencies 80 | Tasks::BundleDependencies.new(context: context).call 81 | end 82 | 83 | def set_bundled_files 84 | Tasks::SetBundledFiles.new(context: context).call 85 | end 86 | 87 | def set_license_file 88 | Tasks::SetLicenseFile.new(context: context).call 89 | end 90 | 91 | def set_required_ruby_version 92 | Tasks::SetRequiredRubyVersion.new(context: context).call 93 | end 94 | end 95 | end 96 | -------------------------------------------------------------------------------- /lib/rsgem/support/Rakefile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | task :code_analysis do 4 | sh 'bundle exec rubocop lib spec' 5 | sh 'bundle exec reek lib' 6 | end 7 | -------------------------------------------------------------------------------- /lib/rsgem/support/github_actions.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | strategy: 9 | matrix: 10 | ruby_version: [2.5.x, 2.6.x, 2.7.x] 11 | 12 | steps: 13 | - uses: actions/checkout@v2 14 | - name: Set up Ruby 15 | uses: actions/setup-ruby@v1 16 | with: 17 | ruby-version: ${{ matrix.ruby_version }} 18 | - name: Calculate variable dynamic values 19 | id: dynamic_values 20 | run: | 21 | echo "::set-output name=installed_ruby_version::$(ruby -e 'print RUBY_VERSION')" 22 | echo "::set-output name=cacheTimeAnchor::$(ruby -e 'require %Q{date}; cacheExpirationSeconds = 60*60*24; print (Time.now.to_i / cacheExpirationSeconds)')" 23 | - name: Download CodeClimate reporter 24 | run: | 25 | curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter 26 | chmod +x ./cc-test-reporter 27 | ./cc-test-reporter before-build 28 | env: 29 | CC_TEST_REPORTER_ID: get_a_test_reporter_id_from_code_climate 30 | - name: Install and config bundler 31 | run: | 32 | gem install bundler:2.1.4 33 | - name: Generate 'Gemfile.lock' before caching gems 34 | run: | 35 | bundle lock --update 36 | - uses: actions/cache@v2 37 | with: 38 | path: vendor/bundle 39 | key: ${{ runner.os }}-ruby_v${{ steps.dynamic_values.outputs.installed_ruby_version }}-time_${{steps.dynamic_values.outputs.cacheTimeAnchor}}-gems-${{ hashFiles('**/Gemfile.lock') }} 40 | - name: Install dependencies 41 | run: | 42 | bundle config set path 'vendor/bundle' 43 | bundle install --jobs 4 --retry 3 44 | - name: Run code analysis 45 | run: | 46 | bundle exec rake code_analysis 47 | - name: Run tests 48 | run: | 49 | bundle exec rspec 50 | - name: Report to CodeClimate 51 | run: | 52 | ./cc-test-reporter after-build --exit-code 0 53 | env: 54 | CC_TEST_REPORTER_ID: get_a_test_reporter_id_from_code_climate 55 | -------------------------------------------------------------------------------- /lib/rsgem/support/reek.yml: -------------------------------------------------------------------------------- 1 | detectors: 2 | Attribute: 3 | enabled: false 4 | exclude: [] 5 | BooleanParameter: 6 | enabled: true 7 | exclude: [] 8 | ClassVariable: 9 | enabled: false 10 | exclude: [] 11 | ControlParameter: 12 | enabled: true 13 | exclude: [] 14 | DataClump: 15 | enabled: true 16 | exclude: [] 17 | max_copies: 2 18 | min_clump_size: 2 19 | DuplicateMethodCall: 20 | enabled: true 21 | exclude: [] 22 | max_calls: 1 23 | allow_calls: [] 24 | FeatureEnvy: 25 | enabled: true 26 | exclude: [] 27 | InstanceVariableAssumption: 28 | enabled: false 29 | IrresponsibleModule: 30 | enabled: false 31 | exclude: [] 32 | LongParameterList: 33 | enabled: true 34 | exclude: [] 35 | max_params: 4 36 | overrides: 37 | initialize: 38 | max_params: 5 39 | LongYieldList: 40 | enabled: true 41 | exclude: [] 42 | max_params: 3 43 | ManualDispatch: 44 | enabled: true 45 | exclude: [] 46 | MissingSafeMethod: 47 | enabled: false 48 | exclude: [] 49 | ModuleInitialize: 50 | enabled: true 51 | exclude: [] 52 | NestedIterators: 53 | enabled: true 54 | exclude: [] 55 | max_allowed_nesting: 2 56 | ignore_iterators: [] 57 | NilCheck: 58 | enabled: false 59 | exclude: [] 60 | RepeatedConditional: 61 | enabled: true 62 | exclude: [] 63 | max_ifs: 3 64 | SubclassedFromCoreClass: 65 | enabled: true 66 | exclude: [] 67 | TooManyConstants: 68 | enabled: true 69 | exclude: [] 70 | max_constants: 5 71 | TooManyInstanceVariables: 72 | enabled: true 73 | exclude: [] 74 | max_instance_variables: 9 75 | TooManyMethods: 76 | enabled: true 77 | exclude: [] 78 | max_methods: 25 79 | TooManyStatements: 80 | enabled: true 81 | exclude: 82 | - initialize 83 | max_statements: 12 84 | UncommunicativeMethodName: 85 | enabled: true 86 | exclude: [] 87 | reject: 88 | - "/^[a-z]$/" 89 | - "/[0-9]$/" 90 | - "/[A-Z]/" 91 | accept: [] 92 | UncommunicativeModuleName: 93 | enabled: true 94 | exclude: [] 95 | reject: 96 | - "/^.$/" 97 | - "/[0-9]$/" 98 | accept: 99 | - Inline::C 100 | - "/V[0-9]/" 101 | UncommunicativeParameterName: 102 | enabled: true 103 | exclude: [] 104 | reject: 105 | - "/^.$/" 106 | - "/[0-9]$/" 107 | - "/[A-Z]/" 108 | accept: [] 109 | UncommunicativeVariableName: 110 | enabled: true 111 | exclude: [] 112 | reject: 113 | - "/^.$/" 114 | - "/[0-9]$/" 115 | - "/[A-Z]/" 116 | accept: 117 | - _ 118 | UnusedParameters: 119 | enabled: true 120 | exclude: [] 121 | UnusedPrivateMethod: 122 | enabled: false 123 | UtilityFunction: 124 | enabled: false 125 | 126 | exclude_paths: 127 | - config 128 | -------------------------------------------------------------------------------- /lib/rsgem/support/rubocop.yml: -------------------------------------------------------------------------------- 1 | inherit_gem: 2 | rubocop-rootstrap: 3 | - config/default_edge.yml 4 | 5 | Gemspec/RequiredRubyVersion: 6 | Enabled: false 7 | 8 | Style/RescueModifier: 9 | Exclude: 10 | - spec/**/* 11 | 12 | Naming/PredicateName: 13 | Enabled: false 14 | 15 | Style/HashEachMethods: 16 | Enabled: true 17 | 18 | Style/HashTransformKeys: 19 | Enabled: true 20 | 21 | Style/HashTransformValues: 22 | Enabled: true 23 | -------------------------------------------------------------------------------- /lib/rsgem/support/travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | 3 | rvm: 4 | - 2.5.0 5 | - 2.6.0 6 | - ruby-head 7 | 8 | dist: bionic 9 | 10 | jobs: 11 | allow_failures: 12 | - rvm: ruby-head 13 | 14 | env: 15 | global: 16 | - CC_TEST_REPORTER_ID=get_a_test_reporter_id_from_code_climate 17 | 18 | before_script: 19 | - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter 20 | - chmod +x ./cc-test-reporter 21 | - ./cc-test-reporter before-build 22 | 23 | script: 24 | - bundle exec rake code_analysis 25 | - bundle exec rspec 26 | 27 | after_script: 28 | - ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT 29 | -------------------------------------------------------------------------------- /lib/rsgem/tasks/add_ci_provider.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module RSGem 4 | module Tasks 5 | class AddCIProvider < Base 6 | OUTPUT = OutputStruct.new(name: :output_name) 7 | 8 | def perform 9 | context.ci_provider.install(context) 10 | end 11 | 12 | def output_name 13 | "Add CI configuration for #{context.ci_provider.display_name}" 14 | end 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /lib/rsgem/tasks/add_code_analysis.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module RSGem 4 | module Tasks 5 | class AddCodeAnalysis < Base 6 | OUTPUT = OutputStruct.new(name: 'Add code analysis') 7 | 8 | def perform 9 | File.open(context.rakefile_path, 'w') do |file| 10 | file.puts rakefile 11 | end 12 | end 13 | 14 | private 15 | 16 | def rakefile 17 | @rakefile ||= File.read("#{File.dirname(__FILE__)}/../support/Rakefile") 18 | end 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /lib/rsgem/tasks/add_dependency.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module RSGem 4 | module Tasks 5 | class AddDependency < Base 6 | OUTPUT = OutputStruct.new(name: :output_name) 7 | 8 | def perform 9 | return if already_installed? 10 | 11 | add_dependency 12 | write_to_gemspec 13 | dependency.install(context) 14 | end 15 | 16 | private 17 | 18 | def add_dependency 19 | gemspec_file.gsub!(/end\n\z/, code) 20 | gemspec_file << "\nend" 21 | end 22 | 23 | def already_installed? 24 | gemspec_file.match? Regexp.new("('|\")#{dependency.name}('|\")") 25 | end 26 | 27 | def code 28 | text = [" spec.add_#{dependency.mode}_dependency '#{dependency.name}'", dependency.version] 29 | text.compact.join(', ') 30 | end 31 | 32 | def dependency 33 | args[:dependency] 34 | end 35 | 36 | def gemspec_file 37 | @gemspec_file ||= File.read(context.gemspec_path) 38 | end 39 | 40 | def output_name 41 | "Install #{dependency.name.capitalize}" 42 | end 43 | 44 | def write_to_gemspec 45 | File.open(context.gemspec_path, 'w') do |file| 46 | file.puts gemspec_file 47 | end 48 | end 49 | end 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /lib/rsgem/tasks/base.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module RSGem 4 | module Tasks 5 | # 6 | # Base class for task objects. 7 | # Child classes must implement the instance method +perform+. 8 | # 9 | class Base 10 | include Output 11 | 12 | attr_reader :context, :args 13 | 14 | def initialize(context:, **args) 15 | @context = context 16 | @args = args 17 | end 18 | 19 | def call 20 | with_output do 21 | perform 22 | end 23 | end 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /lib/rsgem/tasks/bundle_dependencies.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module RSGem 4 | module Tasks 5 | class BundleDependencies < Base 6 | OUTPUT = OutputStruct.new(name: 'Bundle dependencies') 7 | 8 | def perform 9 | return if system("cd #{context.folder_path} && bundle", out: '/dev/null') 10 | 11 | raise RSGem::Errors::Base 12 | end 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /lib/rsgem/tasks/clean_gemfile.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module RSGem 4 | module Tasks 5 | # 6 | # Gemfile should only have the gemspec directive. 7 | # An exception is when you need to develop against a gem that hasn't yet been released, 8 | # in this case you can declare that dependency in the Gemfile: 9 | # 10 | # gem 'rack', github: 'rack/rack' 11 | # 12 | # gemspec is the place to declare dependencies. 13 | # 14 | # https://github.com/rootstrap/tech-guides/blob/master/open-source/developing_gems.md#gemfilegemfilelockgemspec 15 | # 16 | class CleanGemfile < Base 17 | OUTPUT = OutputStruct.new(name: 'Clean gemfile') 18 | 19 | def perform 20 | gemfile.gsub!(/gem .+\n/, '') # Remove all gem definitions 21 | gemfile.sub!(/\n\z/, '') # Remove last new line character 22 | write_to_gemfile 23 | end 24 | 25 | private 26 | 27 | def gemfile 28 | @gemfile ||= File.read(context.gemfile_path) 29 | end 30 | 31 | def write_to_gemfile 32 | File.open(context.gemfile_path, 'w') do |file| 33 | file.puts gemfile 34 | end 35 | end 36 | end 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /lib/rsgem/tasks/clean_gemspec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module RSGem 4 | module Tasks 5 | class CleanGemspec < Base 6 | OUTPUT = OutputStruct.new(name: 'Clean gemspec') 7 | KEYS_TO_EMPTY = %w[summary description homepage].freeze 8 | 9 | def perform 10 | comment_metadata! 11 | empty_keys! 12 | write 13 | end 14 | 15 | private 16 | 17 | def gemspec 18 | @gemspec ||= File.read(context.gemspec_path) 19 | end 20 | 21 | def empty_keys! 22 | KEYS_TO_EMPTY.each do |key| 23 | gemspec.gsub!(/(spec.#{key}.*)=(.*)\n/, "\\1= ''\n") 24 | end 25 | end 26 | 27 | def comment_metadata! 28 | gemspec.gsub!(/spec.metadata/, '# spec.metadata') 29 | end 30 | 31 | def write 32 | File.open(context.gemspec_path, 'w') do |file| 33 | file.puts gemspec 34 | end 35 | end 36 | end 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /lib/rsgem/tasks/create_gem.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module RSGem 4 | module Tasks 5 | class CreateGem < Base 6 | OUTPUT = OutputStruct.new( 7 | name: 'Create gem', 8 | success: :success_message 9 | ) 10 | 11 | def perform 12 | return if system(shell_command, out: '/dev/null') 13 | 14 | raise RSGem::Errors::Base, "Failed to run `bundle gem'. Check bundler is installed in "\ 15 | "your system or install it with `gem install bundler'.`" 16 | end 17 | 18 | private 19 | 20 | def bundler_options 21 | context.bundler_options 22 | end 23 | 24 | def success_message 25 | "Gem created with bundler options: #{bundler_options}" if bundler_options 26 | end 27 | 28 | def shell_command 29 | [ 30 | "bundle gem #{context.gem_name} --test=rspec --coc --mit", 31 | bundler_options 32 | ].compact.join(' ') 33 | end 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /lib/rsgem/tasks/ensure_author.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module RSGem 4 | module Tasks 5 | class EnsureAuthor < Base 6 | OUTPUT = OutputStruct.new( 7 | name: 'Ensure author', 8 | warning: :warning_message 9 | ) 10 | TEMP_USERNAME = 'change_me' 11 | TEMP_EMAIL = 'change_me@notanemail.com' 12 | 13 | def perform 14 | ensure_author 15 | end 16 | 17 | private 18 | 19 | def gemspec 20 | @gemspec ||= File.read(context.gemspec_path) 21 | end 22 | 23 | def write_gemspec 24 | File.write(context.gemspec_path, gemspec) 25 | end 26 | 27 | def ensure_author 28 | return unless missing_git_user? 29 | 30 | gemspec.gsub!(/spec.email\s+=\s+.+/, 31 | "spec.email = ['#{TEMP_EMAIL}']") 32 | 33 | gemspec.gsub!(/spec.authors\s+=\s+.+/, 34 | "spec.authors = ['#{TEMP_USERNAME}']") 35 | write_gemspec 36 | end 37 | 38 | def missing_git_user? 39 | `git config user.email`.strip.empty? || `git config user.name`.strip.empty? 40 | end 41 | 42 | def warning_message 43 | return unless missing_git_user? 44 | 45 | "No git user set. Setting #{TEMP_EMAIL} and #{TEMP_USERNAME} in gemspec, "\ 46 | 'please change this before publishing your gem.' 47 | end 48 | end 49 | end 50 | end 51 | -------------------------------------------------------------------------------- /lib/rsgem/tasks/ignore_gemfile_lock.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module RSGem 4 | module Tasks 5 | # 6 | # Gemfile.lock should be gitignored when developing gems 7 | # 8 | # https://github.com/rootstrap/tech-guides/blob/master/open-source/developing_gems.md#gemfilegemfilelockgemspec 9 | # 10 | class IgnoreGemfileLock < Base 11 | OUTPUT = OutputStruct.new(name: 'Ignore gemfile.lock') 12 | 13 | def perform 14 | gitignore << "\nGemfile.lock\n" 15 | write_to_gitignore 16 | end 17 | 18 | private 19 | 20 | def gitignore 21 | @gitignore ||= File.read(context.gitignore_path) 22 | end 23 | 24 | def write_to_gitignore 25 | File.open(context.gitignore_path, 'w') do |file| 26 | file.puts gitignore 27 | end 28 | end 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /lib/rsgem/tasks/output.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module RSGem 4 | module Tasks 5 | # 6 | # Structure to bundle all messages from a task 7 | # 8 | OutputStruct = Struct.new(:name, :success, :warning, keyword_init: true) 9 | 10 | module Output 11 | def with_output 12 | yield 13 | puts "\t#{Colors.colorize('[OK]', :green)} #{name}" 14 | puts "\t#{success}" if success 15 | puts "\t#{Colors.colorize('Warning: ', :yellow)} #{warning}" if warning 16 | rescue RSGem::Errors::Base => e 17 | puts "\t#{Colors.colorize('[X]', :red)} #{e.message}" 18 | raise e 19 | end 20 | 21 | private 22 | 23 | def deduce_output(value) 24 | case value 25 | when String 26 | value 27 | when Symbol 28 | send(value) 29 | when Proc 30 | value.call 31 | end 32 | end 33 | 34 | def name 35 | deduce_output(self.class::OUTPUT.name || self.class.name) 36 | end 37 | 38 | def success 39 | deduce_output(self.class::OUTPUT.success) 40 | end 41 | 42 | def warning 43 | deduce_output(self.class::OUTPUT.warning) 44 | end 45 | end 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /lib/rsgem/tasks/run_rubocop.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module RSGem 4 | module Tasks 5 | class RunRubocop < Base 6 | OUTPUT = OutputStruct.new(name: 'Run rubocop') 7 | 8 | def perform 9 | return if system("cd #{context.folder_path} && bundle exec rubocop -A", 10 | %i[out err] => '/dev/null') 11 | 12 | raise RSGem::Errors::Base, 'Failed to run `bundle exec rubocop -A\'' 13 | end 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /lib/rsgem/tasks/set_bundled_files.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module RSGem 4 | module Tasks 5 | # 6 | # When the gem is bundled into final users' projects, it only needs to contain the production 7 | # code. Meaning that specs, docs, configuration files should not be present. 8 | # This task updates the default `spec.files` configuration in the gemspec to just contain the 9 | # files: 10 | # - LICENSE.txt 11 | # - README.md 12 | # - lib/**/* (everything inside lib) 13 | # 14 | class SetBundledFiles < Base 15 | OUTPUT = OutputStruct.new(name: 'Set bundled files') 16 | 17 | def perform 18 | # Explaining the regular expression: 19 | # [spec.files][one or more white spaces][=][one or more white spaces][anything until "do"] 20 | # [new line][anything until new line] 21 | # [one or more white spaces][end] 22 | gemspec.gsub!( 23 | /spec.files\s+=\s+.+do\n.+\n\s+end/, 24 | "spec.files = Dir['LICENSE.txt', 'README.md', 'lib/**/*']" 25 | ) 26 | write 27 | end 28 | 29 | private 30 | 31 | def gemspec 32 | @gemspec ||= File.read(context.gemspec_path) 33 | end 34 | 35 | def write 36 | File.open(context.gemspec_path, 'w') do |file| 37 | file.puts gemspec 38 | end 39 | end 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /lib/rsgem/tasks/set_license_file.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module RSGem 4 | module Tasks 5 | class SetLicenseFile < Base 6 | OUTPUT = OutputStruct.new(name: 'Set license file') 7 | 8 | def perform 9 | license.gsub!( 10 | /.*Copyright.*/, 11 | "Copyright (c) #{Date.today.year} Rootstrap" 12 | ) 13 | write 14 | end 15 | 16 | private 17 | 18 | def license 19 | @license ||= File.read(context.license_path) 20 | end 21 | 22 | def write 23 | File.open(context.license_path, 'w') do |file| 24 | file.puts license 25 | end 26 | end 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /lib/rsgem/tasks/set_required_ruby_version.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module RSGem 4 | module Tasks 5 | class SetRequiredRubyVersion < Base 6 | OUTPUT = OutputStruct.new(name: 'Set required Ruby version') 7 | 8 | def perform 9 | set_required_ruby_version 10 | write 11 | end 12 | 13 | private 14 | 15 | def set_required_ruby_version 16 | gemspec.gsub!( 17 | /(spec.required_ruby_version.*)=(.*)\n/, 18 | "spec.required_ruby_version = Gem::Requirement.new('>= 2.5.0')\n" 19 | ) 20 | end 21 | 22 | def gemspec 23 | @gemspec ||= File.read(context.gemspec_path) 24 | end 25 | 26 | def write 27 | File.open(context.gemspec_path, 'w') do |file| 28 | file.puts gemspec 29 | end 30 | end 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /lib/rsgem/tasks/simple_cov_post_install.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module RSGem 4 | module Tasks 5 | class SimpleCovPostInstall < Base 6 | OUTPUT = OutputStruct.new 7 | def perform 8 | spec_helper.sub!("require \"#{gem_name}\"", <<~RUBY) 9 | require 'simplecov' 10 | 11 | SimpleCov.start do 12 | add_filter '/spec/' 13 | end 14 | 15 | require '#{gem_name}' 16 | RUBY 17 | 18 | write 19 | end 20 | 21 | private 22 | 23 | def gem_name 24 | context.gem_name 25 | end 26 | 27 | def spec_helper 28 | @spec_helper ||= File.read(context.spec_helper_path) 29 | end 30 | 31 | def write 32 | File.open(context.spec_helper_path, 'w') do |file| 33 | file.puts spec_helper 34 | end 35 | end 36 | end 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /lib/rsgem/version.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module RSGem 4 | MAJOR = 1 5 | MINOR = 1 6 | PATCH = 0 7 | VERSION = [MAJOR, MINOR, PATCH].join('.') 8 | end 9 | -------------------------------------------------------------------------------- /rsgem.gemspec: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative 'lib/rsgem/version' 4 | 5 | Gem::Specification.new do |spec| 6 | spec.name = 'rsgem' 7 | spec.version = RSGem::VERSION 8 | spec.authors = ['Juan Manuel Ramallo'] 9 | spec.email = ['ramallojuanm@gmail.com'] 10 | 11 | spec.summary = 'Generating gems the Rootstrap way' 12 | spec.homepage = 'https://github.com/rootstrap/rsgem' 13 | spec.license = 'MIT' 14 | spec.required_ruby_version = Gem::Requirement.new('>= 2.3.0') 15 | 16 | spec.metadata['homepage_uri'] = spec.homepage 17 | spec.metadata['source_code_uri'] = 'https://github.com/rootstrap/rsgem' 18 | 19 | # Specify which files should be added to the gem when it is released. 20 | # The `git ls-files -z` loads the files in the RubyGem that have been added into git. 21 | spec.files = Dir['CHANGELOG.md', 'LICENSE.txt', 'README.md', 'bin/**/*', 'lib/**/*'] 22 | spec.bindir = 'exe' 23 | spec.executables = ['rsgem'] 24 | spec.require_paths = ['lib'] 25 | 26 | spec.add_dependency 'dry-cli', '~> 0.6.0' 27 | spec.add_development_dependency 'pry', '~> 0.13.1' 28 | spec.add_development_dependency 'rake', '~> 13.0.1' 29 | spec.add_development_dependency 'rspec', '~> 3.9.0' 30 | spec.add_development_dependency 'rubocop-rootstrap', '~> 1.0.0' 31 | spec.add_development_dependency 'simplecov', '~> 0.17.1' 32 | end 33 | -------------------------------------------------------------------------------- /spec/cli_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | RSpec.describe 'CLI' do 4 | describe 'rsgem version' do 5 | it 'returns the version' do 6 | expect(`./exe/rsgem version`.strip).to eq RSGem::VERSION 7 | end 8 | 9 | it 'returns the version using the alternative -v option' do 10 | expect(`./exe/rsgem -v`.strip).to eq RSGem::VERSION 11 | end 12 | 13 | it 'returns the version using the alternative v option' do 14 | expect(`./exe/rsgem v`.strip).to eq RSGem::VERSION 15 | end 16 | 17 | it 'returns the version using the alternative --version option' do 18 | expect(`./exe/rsgem --version`.strip).to eq RSGem::VERSION 19 | end 20 | end 21 | 22 | describe 'rsgem new NAME --ci=VALUE' do 23 | after { `rm -rf #{gem_name}` } 24 | 25 | let(:gem_name) { 'testing_cli' } 26 | 27 | subject { `./exe/rsgem new #{gem_name}` } 28 | 29 | it 'creates a new gem' do 30 | expect(subject).to include('Install Reek') 31 | expect(subject).to include('Install Rubocop') 32 | expect(subject).to include('Install Simplecov') 33 | expect(subject).to include('Clean gemfile') 34 | expect(subject).to include('Ignore gemfile.lock') 35 | expect(subject).to include('Add CI configuration for Github Actions') 36 | expect(subject).to include('Bundle dependencies') 37 | expect(subject).to include('Run rubocop') 38 | expect(File.exist?(gem_name)).to eq true 39 | end 40 | 41 | context 'with travis as the CI provider' do 42 | subject { `./exe/rsgem new #{gem_name} --ci=travis` } 43 | 44 | it 'creates a new gem with travis configuration' do 45 | expect(subject).to include('Add CI configuration for Travis') 46 | expect(File.exist?("#{gem_name}/.travis.yml")).to eq true 47 | end 48 | end 49 | 50 | context 'with github actions as the CI provider' do 51 | subject { `./exe/rsgem new #{gem_name} --ci=github_actions` } 52 | 53 | it 'creates a new gem with github actions configuration' do 54 | expect(subject).to include('Add CI configuration for Github Actions') 55 | expect(File.exist?("#{gem_name}/.github/workflows/ci.yml")).to eq true 56 | end 57 | end 58 | 59 | context 'github config' do 60 | subject { `./exe/rsgem new #{gem_name} --ci=github_actions` } 61 | 62 | context 'is set' do 63 | it 'does not issue a warning or modify the gemspec' do 64 | expect(subject).to_not match( 65 | /Warning: No git username set, setting change_me for now/ 66 | ) 67 | expect(subject).to_not match( 68 | /Warning: No git email set, setting change_me@notanemail.com for now/ 69 | ) 70 | end 71 | end 72 | 73 | context 'not set' do 74 | let!(:previous_git_user_name) { `git config user.name`.strip } 75 | let!(:previous_git_user_email) { `git config user.email`.strip } 76 | 77 | before do 78 | `git config user.name ""` 79 | `git config user.email ""` 80 | end 81 | 82 | it 'creates a new gem with placeholder information' do 83 | expect(subject).to match( 84 | /No git user set./ 85 | ) 86 | end 87 | 88 | after do 89 | `git config user.name "#{previous_git_user_name}"` 90 | `git config user.email "#{previous_git_user_email}"` 91 | end 92 | end 93 | end 94 | end 95 | 96 | describe 'rsgem new NAME --bundler=VALUE' do 97 | after { `rm -rf #{gem_name}` } 98 | 99 | let(:gem_name) { 'testing_cli' } 100 | let(:options) { '--exe' } 101 | 102 | subject { `./exe/rsgem new #{gem_name} --bundler=#{options}` } 103 | 104 | it { is_expected.to include('Gem created with bundler options: --exe') } 105 | 106 | context 'with two options' do 107 | let(:options) { "'--exe --ext'" } 108 | 109 | it { is_expected.to include('Gem created with bundler options: --exe --ext') } 110 | end 111 | end 112 | 113 | describe 'rsgem new NAME --bundler=VALUE --ci=VALUE' do 114 | after { `rm -rf #{gem_name}` } 115 | 116 | let(:gem_name) { 'testing_cli' } 117 | 118 | subject { `./exe/rsgem new #{gem_name} --bundler=--ext --ci=github_actions` } 119 | 120 | it 'works with both options' do 121 | expect(subject).to include('Gem created with bundler options: --ext') 122 | expect(subject).to include('Add CI configuration for Github Actions') 123 | end 124 | end 125 | end 126 | -------------------------------------------------------------------------------- /spec/gem_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | RSpec.describe RSGem::Gem do 4 | gem_name = 'test' 5 | 6 | describe '#create' do 7 | context 'with default configuration' do 8 | before(:all) do 9 | described_class.new(gem_name: gem_name).create 10 | end 11 | after(:all) do 12 | `rm -rf ./#{gem_name}` 13 | end 14 | 15 | let(:list_files) { `ls -a`.split("\n") } 16 | let(:list_files_gem) { `ls -a #{gem_name}`.split("\n") } 17 | let(:gemspec) { File.read("./#{gem_name}/#{gem_name}.gemspec") } 18 | let(:gemfile) { File.read("./#{gem_name}/Gemfile") } 19 | let(:gitignore) { File.read("./#{gem_name}/.gitignore") } 20 | let(:rakefile) { File.read("./#{gem_name}/Rakefile") } 21 | let(:spec_helper) { File.read("./#{gem_name}/spec/spec_helper.rb") } 22 | let(:license) { File.read("./#{gem_name}/LICENSE.txt") } 23 | let(:github_actions) { File.read("./#{gem_name}/.github/workflows/ci.yml") } 24 | let(:expected_github_actions) { File.read('./lib/rsgem/support/github_actions.yml') } 25 | 26 | it 'creates a new folder' do 27 | expect(list_files).to include gem_name 28 | end 29 | 30 | it 'adds reek to the gemspec' do 31 | expect(gemspec).to include "spec.add_development_dependency 'reek'" 32 | end 33 | 34 | it 'adds rubocop to the gemspec' do 35 | expect(gemspec).to include "spec.add_development_dependency 'rubocop-rootstrap'" 36 | end 37 | 38 | it 'adds simplecov to the gemspec' do 39 | expect(gemspec).to include "spec.add_development_dependency 'simplecov', '~> 0.17.1'" 40 | end 41 | 42 | it 'adds rake to the gemspec if needed' do 43 | expect(gemspec.scan('rake').size).to eq 1 44 | end 45 | 46 | it 'adds rspec to the gemspec if needed' do 47 | expect(gemspec.scan('rspec').size).to eq 1 48 | end 49 | 50 | it 'assigns a version to simplecov' do 51 | expect(gemspec).to include "'simplecov', '~> 0.17.1'" 52 | end 53 | 54 | it 'adds the rubocop config file' do 55 | expect(list_files_gem).to include '.rubocop.yml' 56 | end 57 | 58 | it 'adds the reek config file' do 59 | expect(list_files_gem).to include '.rubocop.yml' 60 | end 61 | 62 | it 'removes the default gems from the gemfile' do 63 | expect(gemfile).not_to match(/gem /) 64 | end 65 | 66 | it 'includes the base gemspec in the gemfile' do 67 | expect(gemfile).to include 'gemspec' 68 | end 69 | 70 | it 'leaves only one last new line character in the gemfile' do 71 | expect(gemfile[gemfile.size - 2..gemfile.size].count("\n")).to eq 1 72 | end 73 | 74 | it 'adds the Gemfile.lock file into the git ignored files' do 75 | expect(gitignore).to include 'Gemfile.lock' 76 | end 77 | 78 | it 'adds the code analysis rake task to the Rakefile' do 79 | expect(rakefile).to include( 80 | <<~RUBY 81 | task :code_analysis do 82 | sh 'bundle exec rubocop lib spec' 83 | sh 'bundle exec reek lib' 84 | end 85 | RUBY 86 | ) 87 | end 88 | 89 | it 'updates the gemspec files config' do 90 | expect(gemspec).to include "spec.files = Dir['LICENSE.txt', 'README.md', 'lib/**/*']" 91 | end 92 | 93 | it 'adds github actions configuration file' do 94 | expect(github_actions).to eq expected_github_actions 95 | end 96 | 97 | it 'does not create a travis configuration file' do 98 | expect(File.exist?("./#{gem_name}/.travis.yml")).to eq false 99 | end 100 | 101 | it 'adds simplecov configuration in spec helper file' do 102 | expect(spec_helper).to include 'SimpleCov.start do' 103 | end 104 | 105 | it 'adds license file with Rootstrap name' do 106 | expect(license).to include "Copyright (c) #{Date.today.year} Rootstrap" 107 | end 108 | 109 | it 'sets the required Ruby version to 2.5' do 110 | expect(gemspec).to include "Gem::Requirement.new('>= 2.5.0')" 111 | end 112 | end 113 | 114 | context 'with travis as ci provider' do 115 | before(:all) do 116 | described_class.new(gem_name: gem_name, ci_provider: 'travis').create 117 | end 118 | after(:all) do 119 | `rm -rf ./#{gem_name}` 120 | end 121 | 122 | let(:travis) { File.read("./#{gem_name}/.travis.yml") } 123 | let(:expected_travis) { File.read('./lib/rsgem/support/travis.yml') } 124 | 125 | it 'adds travis configuration file' do 126 | expect(travis).to eq expected_travis 127 | end 128 | 129 | it 'does not create a github actions configuration file' do 130 | expect(File.exist?("./#{gem_name}/.github/workflows")).to eq false 131 | end 132 | end 133 | 134 | context 'when gem name options is missing' do 135 | it 'raises a missing gem name error' do 136 | expect { described_class.new({}).create }.to raise_error(RSGem::Errors::MissingGemName) 137 | end 138 | end 139 | 140 | context 'using bundler options' do 141 | before(:all) do 142 | described_class.new(gem_name: gem_name, bundler_options: '--ext --exe').create 143 | end 144 | after(:all) do 145 | `rm -rf ./#{gem_name}` 146 | end 147 | 148 | it 'adds the exe and ext folders' do 149 | expect(File.exist?("./#{gem_name}/exe")).to eq true 150 | expect(File.exist?("./#{gem_name}/ext")).to eq true 151 | end 152 | end 153 | 154 | context 'with failing bundler' do 155 | after { `rm -rf ./#{gem_name}` } 156 | 157 | it 'exits the process' do 158 | expect_any_instance_of(Object).to receive(:system).and_return(false) 159 | expect { described_class.new(gem_name: gem_name).create }.to( 160 | raise_error(RSGem::Errors::Base) 161 | ) 162 | end 163 | end 164 | end 165 | end 166 | -------------------------------------------------------------------------------- /spec/rsgem_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | RSpec.describe RSGem do 4 | it 'has a version number' do 5 | expect(RSGem::VERSION).not_to be nil 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'bundler/setup' 4 | require 'date' 5 | require 'simplecov' 6 | require 'pry' 7 | require 'rake' 8 | 9 | SimpleCov.start do 10 | add_filter '/spec/' 11 | end 12 | 13 | require 'rsgem' 14 | 15 | RSpec.configure do |config| 16 | # Enable flags like --only-failures and --next-failure 17 | config.example_status_persistence_file_path = '.rspec_status' 18 | 19 | # Disable RSpec exposing methods globally on `Module` and `main` 20 | config.disable_monkey_patching! 21 | 22 | config.expect_with :rspec do |c| 23 | c.syntax = :expect 24 | end 25 | 26 | original_stderr = $stderr 27 | original_stdout = $stdout 28 | config.before(:all) do 29 | $stderr = File.open(File::NULL, 'w') 30 | $stdout = File.open(File::NULL, 'w') 31 | @previous_git_user_name = `git config user.name`.strip 32 | @previous_git_user_email = `git config user.email`.strip 33 | `git config user.name Testing` 34 | `git config user.email testing@example.com` 35 | end 36 | 37 | config.after(:all) do 38 | `git config user.name '#{@previous_git_user_name}'` 39 | `git config user.email '#{@previous_git_user_email}'` 40 | $stderr = original_stderr 41 | $stdout = original_stdout 42 | end 43 | end 44 | --------------------------------------------------------------------------------