nextgen 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +143 -0
- data/config/generators.yml +146 -0
- data/exe/nextgen +13 -0
- data/lib/nextgen/actions/bundler.rb +62 -0
- data/lib/nextgen/actions/git.rb +56 -0
- data/lib/nextgen/actions/yarn.rb +32 -0
- data/lib/nextgen/actions.rb +155 -0
- data/lib/nextgen/cli.rb +19 -0
- data/lib/nextgen/commands/create.rb +312 -0
- data/lib/nextgen/ext/prompt/list.rb +13 -0
- data/lib/nextgen/ext/prompt/multilist.rb +16 -0
- data/lib/nextgen/generators/action_mailer.rb +26 -0
- data/lib/nextgen/generators/annotate.rb +2 -0
- data/lib/nextgen/generators/base.rb +60 -0
- data/lib/nextgen/generators/basic_auth.rb +5 -0
- data/lib/nextgen/generators/brakeman.rb +2 -0
- data/lib/nextgen/generators/bundler_audit.rb +2 -0
- data/lib/nextgen/generators/capybara_lockstep.rb +4 -0
- data/lib/nextgen/generators/clean_gemfile.rb +1 -0
- data/lib/nextgen/generators/dotenv.rb +3 -0
- data/lib/nextgen/generators/erb_lint.rb +11 -0
- data/lib/nextgen/generators/eslint.rb +24 -0
- data/lib/nextgen/generators/factory_bot_rails.rb +16 -0
- data/lib/nextgen/generators/git_safe.rb +4 -0
- data/lib/nextgen/generators/github_actions.rb +2 -0
- data/lib/nextgen/generators/good_migrations.rb +1 -0
- data/lib/nextgen/generators/home_controller.rb +3 -0
- data/lib/nextgen/generators/initial_git_commit.rb +5 -0
- data/lib/nextgen/generators/initial_migrations.rb +11 -0
- data/lib/nextgen/generators/letter_opener.rb +8 -0
- data/lib/nextgen/generators/node.rb +5 -0
- data/lib/nextgen/generators/open_browser_on_start.rb +12 -0
- data/lib/nextgen/generators/overcommit.rb +1 -0
- data/lib/nextgen/generators/pgcli_rails.rb +1 -0
- data/lib/nextgen/generators/rack_canonical_host.rb +8 -0
- data/lib/nextgen/generators/rack_mini_profiler.rb +2 -0
- data/lib/nextgen/generators/rspec_rails.rb +19 -0
- data/lib/nextgen/generators/rspec_system_testing.rb +5 -0
- data/lib/nextgen/generators/rubocop.rb +32 -0
- data/lib/nextgen/generators/shoulda.rb +6 -0
- data/lib/nextgen/generators/sidekiq.rb +30 -0
- data/lib/nextgen/generators/stylelint.rb +24 -0
- data/lib/nextgen/generators/thor.rb +2 -0
- data/lib/nextgen/generators/tomo.rb +10 -0
- data/lib/nextgen/generators/vite.rb +103 -0
- data/lib/nextgen/generators.rb +87 -0
- data/lib/nextgen/rails.rb +39 -0
- data/lib/nextgen/rails_options.rb +191 -0
- data/lib/nextgen/thor_extensions.rb +48 -0
- data/lib/nextgen/tidy_gemfile.rb +71 -0
- data/lib/nextgen/version.rb +3 -0
- data/lib/nextgen.rb +17 -0
- data/template/.editorconfig +14 -0
- data/template/.env.sample +2 -0
- data/template/.erb-lint.yml.tt +25 -0
- data/template/.eslintrc.cjs +26 -0
- data/template/.github/workflows/ci.yml.tt +142 -0
- data/template/.overcommit.yml.tt +86 -0
- data/template/.prettierrc.cjs +6 -0
- data/template/.rubocop.yml.tt +269 -0
- data/template/.stylelintrc.cjs +52 -0
- data/template/DEPLOYMENT.md +10 -0
- data/template/Procfile.tt +4 -0
- data/template/README.md.tt +52 -0
- data/template/Thorfile +7 -0
- data/template/app/controllers/concerns/basic_auth.rb +20 -0
- data/template/app/controllers/home_controller.rb +4 -0
- data/template/app/frontend/controllers/index.js +5 -0
- data/template/app/frontend/images/example.svg +3 -0
- data/template/app/frontend/stylesheets/base.css +8 -0
- data/template/app/frontend/stylesheets/index.css +3 -0
- data/template/app/frontend/stylesheets/reset.css +36 -0
- data/template/app/helpers/inline_svg_helper.rb +9 -0
- data/template/app/views/home/index.html.erb.tt +5 -0
- data/template/bin/setup +107 -0
- data/template/config/initializers/generators.rb +5 -0
- data/template/config/initializers/rack_mini_profiler.rb +4 -0
- data/template/config/initializers/sidekiq.rb +32 -0
- data/template/config/sidekiq.yml +5 -0
- data/template/lib/puma/plugin/open.rb +14 -0
- data/template/lib/tasks/auto_annotate_models.rake +52 -0
- data/template/lib/tasks/erblint.rake +11 -0
- data/template/lib/tasks/eslint.rake +11 -0
- data/template/lib/tasks/rubocop.rake +4 -0
- data/template/lib/tasks/stylelint.rake +11 -0
- data/template/lib/templates/rspec/system/system_spec.rb +10 -0
- data/template/lib/vite_inline_svg_file_loader.rb +25 -0
- data/template/package.json +6 -0
- data/template/postcss.config.js +3 -0
- data/template/spec/support/factory_bot.rb +3 -0
- data/template/spec/support/mailer.rb +5 -0
- data/template/spec/support/shoulda.rb +6 -0
- data/template/spec/support/system.rb +17 -0
- data/template/test/application_system_test_case.rb +18 -0
- data/template/test/helpers/inline_svg_helper_test.rb +23 -0
- data/template/test/support/factory_bot.rb +3 -0
- data/template/test/support/mailer.rb +3 -0
- data/template/test/support/shoulda.rb +6 -0
- data/template/test/support/vite.rb +5 -0
- metadata +220 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: b5b46f28e5dece5c8f9e57335f0bd0d1d6093011d26d0fb7349a5457a47ae84d
|
4
|
+
data.tar.gz: 999357e7460f394bbb8131e70066064533b6235739104065bba22e2298d60646
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5ceb53aceb86cb606641e8ef7c2e22678a968aa4fe45c1997673122e7f3c14cdda6ab6975d48df3b765ba300954664098fc3cc89f386fb7c1ab6f36aebb16790
|
7
|
+
data.tar.gz: 9025c715d847630295d952c54c1827b89c012df9f7fc42b22c69ee2fc3708e836acb08f877eca821ed542cafab39b49ce16bc9dabc927b503d75f383cc02a5d7
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2023 Matt Brictson
|
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.
|
data/README.md
ADDED
@@ -0,0 +1,143 @@
|
|
1
|
+
# Nextgen
|
2
|
+
|
3
|
+
[![Gem Version](https://img.shields.io/gem/v/nextgen)](https://rubygems.org/gems/nextgen)
|
4
|
+
[![Gem Downloads](https://img.shields.io/gem/dt/nextgen)](https://www.ruby-toolbox.com/projects/nextgen)
|
5
|
+
[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/mattbrictson/nextgen/ci.yml)](https://github.com/mattbrictson/nextgen/actions/workflows/ci.yml)
|
6
|
+
[![Code Climate maintainability](https://img.shields.io/codeclimate/maintainability/mattbrictson/nextgen)](https://codeclimate.com/github/mattbrictson/nextgen)
|
7
|
+
|
8
|
+
Nextgen is an **interactive** and flexible alternative to `rails new` that includes opt-in support for modern frontend development with ⚡️**Vite.**
|
9
|
+
|
10
|
+
[**About**](#about)
|
11
|
+
|
|
12
|
+
[**Requirements**](#requirements)
|
13
|
+
|
|
14
|
+
[**Usage**](#usage)
|
15
|
+
|
|
16
|
+
[**What's included**](#whats-included)<br>
|
17
|
+
[Support](#support)
|
18
|
+
|
|
19
|
+
[License](#license)
|
20
|
+
|
|
21
|
+
[Code of conduct](#code-of-conduct)
|
22
|
+
|
|
23
|
+
[Contribution guide](#contribution-guide)
|
24
|
+
|
25
|
+
TODO: screen recording goes here
|
26
|
+
|
27
|
+
## About
|
28
|
+
|
29
|
+
Nextgen is an interactive and flexible Rails app generator that starts with the official recommendations of `rails new`, and then lets you apply dozens of best-practices, tweaks, documentation, and personal preferences. Opt out of things you don't need (Action Mailbox?), choose your test framework and database, and opt into a curated set of additional gems like FactoryBot and RuboCop configured with reasonable defaults.
|
30
|
+
|
31
|
+
If you need to use React or (gasp!) TypeScript alongside Rails, Nextgen also has you covered with its ability to generate a robust Vite + Rails setup. [Vite](https://vite-ruby.netlify.app) brings excellent frontend tooling that goes well beyond what the default Rails stack can provide.
|
32
|
+
|
33
|
+
Nextgen is maintained by [@mattbrictson](https://github.com/mattbrictson) and is the tool I use to generate all of my personal and professional Rails projects. For more details, check out [what's included](#whats-included) below, or peruse the [examples directory](./examples) of apps generated by Nextgen.
|
34
|
+
|
35
|
+
## Requirements
|
36
|
+
|
37
|
+
Nextgen generates apps using **Rails 7.1**.
|
38
|
+
|
39
|
+
- **Ruby 3.0+** is required
|
40
|
+
- **Rubygems 3.4.8+** is required (run `gem update --system` to get it)
|
41
|
+
- **Node 18+ and Yarn** are required if you choose Vite or other Node-based options
|
42
|
+
- Additional tools may be required depending on the options you select (e.g. PostgreSQL)
|
43
|
+
|
44
|
+
Going forward, my goal is that Nextgen will always target the latest stable version of Rails and the next pre-release version. Support for Node LTS and Ruby versions will be dropped as soon as they reach EOL (see [Ruby](https://endoflife.date/ruby) and [Node](https://endoflife.date/nodejs) EOL schedules).
|
45
|
+
|
46
|
+
## Usage
|
47
|
+
|
48
|
+
To create a new Rails app with Nextgen, run:
|
49
|
+
|
50
|
+
```
|
51
|
+
gem exec nextgen create myapp
|
52
|
+
```
|
53
|
+
|
54
|
+
This will download the latest version of the `nextgen` gem and use it to create an app in the `myapp` directory. You'll be asked to configure the tech stack through several interactive prompts. If you have a `~/.railsrc` file, it will be ignored.
|
55
|
+
|
56
|
+
> [!NOTE]
|
57
|
+
> If you get an "Unknown command exec" error, fix it by upgrading rubygems: `gem update --system`.
|
58
|
+
|
59
|
+
## What's included
|
60
|
+
|
61
|
+
**Nextgen starts with the "omakase" default behavior of `rails new`,** so you get the great things included in Rails 7.1 like a production-ready Dockerfile, your choice of database platform, CSS framework, etc. You can also interactively disable parts of the default stack that you don't need, like JBuilder or Action Mailbox.
|
62
|
+
|
63
|
+
On top of that foundation, Nextgen offers dozens of useful enhancements to the vanilla Rails experience. You are free to pick and choose which (if any) of these to apply to your new project. Behind the scenes, **each enhancement is applied in a separate git commit,** so that you can later see what was applied and why, and revert the suggestions if necessary.
|
64
|
+
|
65
|
+
> [!NOTE]
|
66
|
+
> For the full list of what Nextgen provides, check out [config/generators.yml](https://github.com/mattbrictson/nextgen/tree/main/config/generators.yml). The source code of each generator can be found in [lib/nextgen/generators](https://github.com/mattbrictson/nextgen/tree/main/lib/nextgen/generators).
|
67
|
+
|
68
|
+
Here are some highlights of what Nextgen brings to the table:
|
69
|
+
|
70
|
+
### GitHub Actions
|
71
|
+
|
72
|
+
Nextgen can optionally set up a GitHub Actions CI workflow for your app that automatically runs tests, linters, and security checks based on the gems and packages you have installed.
|
73
|
+
|
74
|
+
### Minitest or RSpec
|
75
|
+
|
76
|
+
Prefer RSpec? Nextgen can set you up with RSpec, plus the gems and configuration you need for system specs (browser testing). Or stick with the Rails Minitest defaults. In either case, Nextgen will set up a good default Rake task and appropriate CI job.
|
77
|
+
|
78
|
+
### Gems
|
79
|
+
|
80
|
+
Nextgen can install and configure your choice of these recommended gems:
|
81
|
+
|
82
|
+
- [annotate](https://github.com/ctran/annotate_models)
|
83
|
+
- [brakeman](https://github.com/presidentbeef/brakeman)
|
84
|
+
- [bundler-audit](https://github.com/rubysec/bundler-audit)
|
85
|
+
- [capybara-lockstep](https://github.com/makandra/capybara-lockstep)
|
86
|
+
- [dotenv-rails](https://github.com/bkeepers/dotenv)
|
87
|
+
- [erb_lint](https://github.com/Shopify/erb-lint)
|
88
|
+
- [factory_bot_rails](https://github.com/thoughtbot/factory_bot_rails)
|
89
|
+
- [good_migrations](https://github.com/testdouble/good-migrations)
|
90
|
+
- [letter_opener](https://github.com/ryanb/letter_opener)
|
91
|
+
- [overcommit](https://github.com/sds/overcommit)
|
92
|
+
- [pgcli-rails](https://github.com/mattbrictson/pgcli-rails)
|
93
|
+
- [rack-canonical-host](https://github.com/tylerhunt/rack-canonical-host)
|
94
|
+
- [rack-mini-profiler](https://github.com/MiniProfiler/rack-mini-profiler)
|
95
|
+
- [rubocop](https://github.com/rubocop/rubocop)
|
96
|
+
- [shoulda-matchers](https://github.com/thoughtbot/shoulda-matchers)
|
97
|
+
- [sidekiq](https://github.com/sidekiq/sidekiq)
|
98
|
+
- [thor](https://github.com/rails/thor)
|
99
|
+
- [tomo](https://github.com/mattbrictson/tomo)
|
100
|
+
|
101
|
+
### Node packages
|
102
|
+
|
103
|
+
Nextgen can optionally install and configure these Node packages to work nicely with Rails:
|
104
|
+
|
105
|
+
- [eslint](https://github.com/eslint/eslint)
|
106
|
+
- [stylelint](https://github.com/stylelint/stylelint)
|
107
|
+
|
108
|
+
### Vite
|
109
|
+
|
110
|
+
[Vite](https://vitejs.dev) (pronounced _veet_) is a next generation Node-based frontend build system and hot-reloading dev server. It can completely take the place of the asset pipeline and integrate seamlessly with Rails via the [vite_rails](https://github.com/ElMassimo/vite_ruby) gem. If you opt-in, Nextgen can set you up with Vite and adds a bunch of vite_rails best practices:
|
111
|
+
|
112
|
+
- All frontend sources (CSS, JS, images) are moved to `app/frontend`
|
113
|
+
- A `yarn start` script is used to start the Rails server and Vite dev server
|
114
|
+
- The [autoprefixer](https://github.com/postcss/autoprefixer) package is installed and activated via PostCSS
|
115
|
+
- A base set of CSS files are added, including [modern_normalize](https://github.com/sindresorhus/modern-normalize)
|
116
|
+
- A Vite-compatible inline SVG helper is added
|
117
|
+
- The [stimulus-vite-helpers](https://github.com/ElMassimo/stimulus-vite-helpers) package is installed (if Stimulus is detected)
|
118
|
+
- `vite-plugin-ruby` is replaced with the more full-featured `vite-plugin-rails`
|
119
|
+
- Sprockets is removed and the asset pipeline is disabled
|
120
|
+
- Vite's `autoBuild` is turned off for the test environment
|
121
|
+
|
122
|
+
These recommendations are based on my experience using Vite with Rails in multiple production apps. For additional background on these and other Vite-related changes, check out the following blog posts:
|
123
|
+
|
124
|
+
- [How to organize CSS in a Rails project](https://mattbrictson.com/blog/organizing-css-in-rails)
|
125
|
+
- [Fixing slow, flaky system tests in Vite-Rails](https://mattbrictson.com/blog/faster-vite-test-without-autobuild)
|
126
|
+
- [The 3 Vite plugins I use on every new Rails project](https://mattbrictson.com/blog/3-vite-rails-plugins)
|
127
|
+
- [Inline SVGs with Rails and Vite](https://mattbrictson.com/blog/inline-svg-with-vite-rails)
|
128
|
+
|
129
|
+
## Support
|
130
|
+
|
131
|
+
If you want to report a bug, or have ideas, feedback or questions about Nextgen, [let me know via GitHub issues](https://github.com/mattbrictson/nextgen/issues/new) and I will do my best to provide a helpful answer. Happy hacking!
|
132
|
+
|
133
|
+
## License
|
134
|
+
|
135
|
+
Nextgen is available as open source under the terms of the [MIT License](LICENSE.txt).
|
136
|
+
|
137
|
+
## Code of conduct
|
138
|
+
|
139
|
+
Everyone interacting in this project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](CODE_OF_CONDUCT.md).
|
140
|
+
|
141
|
+
## Contribution guide
|
142
|
+
|
143
|
+
Pull requests are welcome!
|
@@ -0,0 +1,146 @@
|
|
1
|
+
initial_git_commit:
|
2
|
+
|
3
|
+
base:
|
4
|
+
description: "Enhance base Rails template with better docs, etc"
|
5
|
+
|
6
|
+
initial_migrations:
|
7
|
+
description: "Install and run initial migrations"
|
8
|
+
requires: active_record
|
9
|
+
|
10
|
+
clean_gemfile:
|
11
|
+
description: "Clean up Gemfile"
|
12
|
+
|
13
|
+
rspec_rails:
|
14
|
+
description: "Install and configure rspec-rails"
|
15
|
+
requires: rspec
|
16
|
+
|
17
|
+
rspec_system_testing:
|
18
|
+
description: "Install capybara + selenium-webdriver and set up system specs"
|
19
|
+
requires:
|
20
|
+
- rspec
|
21
|
+
- system_testing
|
22
|
+
|
23
|
+
node:
|
24
|
+
description: "Set up Node and Yarn"
|
25
|
+
|
26
|
+
vite:
|
27
|
+
description: "Replace the asset pipeline with Vite in app/frontend"
|
28
|
+
requires: vite
|
29
|
+
node: true
|
30
|
+
|
31
|
+
action_mailer:
|
32
|
+
description: "Improve Action Mailer support for absolute URLs and testing"
|
33
|
+
requires: action_mailer
|
34
|
+
|
35
|
+
annotate:
|
36
|
+
prompt: "Annotate Models"
|
37
|
+
description: "Install annotate gem to auto-generate schema annotations"
|
38
|
+
requires: active_record
|
39
|
+
|
40
|
+
basic_auth:
|
41
|
+
prompt: "BasicAuth controller concern"
|
42
|
+
description: "Allow app to be secured with ENV-based basic auth credentials"
|
43
|
+
|
44
|
+
brakeman:
|
45
|
+
prompt: "Brakeman"
|
46
|
+
description: "Install brakeman gem for security checks"
|
47
|
+
|
48
|
+
bundler_audit:
|
49
|
+
prompt: "Bundler Audit"
|
50
|
+
description: "Install bundler-audit gem to detect CVEs in Ruby dependencies"
|
51
|
+
|
52
|
+
capybara_lockstep:
|
53
|
+
prompt: "capybara-lockstep"
|
54
|
+
description: "Install capybara-lockstep gem for less-flaky browser testing"
|
55
|
+
requires: system_testing
|
56
|
+
|
57
|
+
dotenv:
|
58
|
+
prompt: "dotenv-rails"
|
59
|
+
description: "Install dotenv-rails gem and add .env.sample"
|
60
|
+
|
61
|
+
erb_lint:
|
62
|
+
prompt: "ERB Lint"
|
63
|
+
description: "Install erb_lint gem and correct existing issues"
|
64
|
+
requires: frontend
|
65
|
+
|
66
|
+
eslint:
|
67
|
+
prompt: "ESLint"
|
68
|
+
description: "Install eslint + supporting packages; apply prettier format"
|
69
|
+
requires: frontend
|
70
|
+
node: true
|
71
|
+
|
72
|
+
factory_bot_rails:
|
73
|
+
prompt: "Factory Bot"
|
74
|
+
description: "Install and configure factory_bot_rails gem"
|
75
|
+
requires: active_record
|
76
|
+
|
77
|
+
git_safe:
|
78
|
+
|
79
|
+
good_migrations:
|
80
|
+
prompt: "good_migrations"
|
81
|
+
description: "Install good_migrations gem"
|
82
|
+
requires: active_record
|
83
|
+
|
84
|
+
home_controller:
|
85
|
+
description: "Create a controller, view, and route for the home page"
|
86
|
+
requires: frontend
|
87
|
+
|
88
|
+
letter_opener:
|
89
|
+
prompt: "letter_opener"
|
90
|
+
description: "Install letter_opener gem to use with Action Mailer in dev"
|
91
|
+
requires: action_mailer
|
92
|
+
|
93
|
+
open_browser_on_start:
|
94
|
+
prompt: "Open browser on startup"
|
95
|
+
description: "Configure puma to launch browser on startup in development"
|
96
|
+
requires: frontend
|
97
|
+
|
98
|
+
pgcli_rails:
|
99
|
+
prompt: "pgcli_rails"
|
100
|
+
description: "Install pgcli_rails gem to allow easy use of pgcli"
|
101
|
+
requires: postgresql
|
102
|
+
|
103
|
+
rack_canonical_host:
|
104
|
+
prompt: "rack-canonical-host"
|
105
|
+
description: "Install rack-canonical-host gem; use RAILS_HOSTNAME"
|
106
|
+
|
107
|
+
rack_mini_profiler:
|
108
|
+
prompt: "rack-mini-profiler"
|
109
|
+
description: "Install rack-mini-profiler gem in development"
|
110
|
+
requires: frontend
|
111
|
+
|
112
|
+
shoulda:
|
113
|
+
prompt: "shoulda"
|
114
|
+
description: "Install shoulda-matchers gem for concise model testing"
|
115
|
+
requires: test_framework
|
116
|
+
|
117
|
+
sidekiq:
|
118
|
+
prompt: "Sidekiq"
|
119
|
+
description: "Install sidekiq gem to use in production"
|
120
|
+
requires: active_job
|
121
|
+
|
122
|
+
stylelint:
|
123
|
+
prompt: "Stylelint"
|
124
|
+
description: "Install stylelint and apply prettier format to CSS"
|
125
|
+
requires: frontend
|
126
|
+
node: true
|
127
|
+
|
128
|
+
thor:
|
129
|
+
prompt: "Thor"
|
130
|
+
description: "Configure Thor for ease of writing Rails tasks"
|
131
|
+
|
132
|
+
tomo:
|
133
|
+
prompt: "Tomo"
|
134
|
+
description: "Install tomo gem for SSH-based deployment"
|
135
|
+
|
136
|
+
rubocop:
|
137
|
+
prompt: "RuboCop"
|
138
|
+
description: "Install rubocop gems; apply formatting rules"
|
139
|
+
|
140
|
+
overcommit:
|
141
|
+
prompt: "Overcommit"
|
142
|
+
description: "Configure overcommit pre-commit git hooks"
|
143
|
+
|
144
|
+
github_actions:
|
145
|
+
prompt: "GitHub Actions"
|
146
|
+
description: "Configure GitHub Actions workflow for CI"
|
data/exe/nextgen
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "nextgen"
|
4
|
+
|
5
|
+
# Work around an "unresolved or ambiguous specs" warning when CLI is run via `gem exec`
|
6
|
+
# https://github.com/rubygems/rubygems/issues/6914
|
7
|
+
at_exit do
|
8
|
+
Gem.finish_resolve
|
9
|
+
rescue Exception # rubocop:disable Lint/RescueException
|
10
|
+
# ignore
|
11
|
+
end
|
12
|
+
|
13
|
+
Nextgen::CLI.start
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Nextgen
|
2
|
+
module Actions::Bundler
|
3
|
+
def binstubs(*gems)
|
4
|
+
bundle_command! "binstubs #{gems.join(" ")} --force"
|
5
|
+
end
|
6
|
+
alias binstub binstubs
|
7
|
+
|
8
|
+
def install_gems(*names, version: nil, group: nil, require: nil)
|
9
|
+
gemfile = TidyGemfile.new
|
10
|
+
inserted = names.filter do |name|
|
11
|
+
if gemfile.include?(name)
|
12
|
+
say_status :exist, name, :blue
|
13
|
+
false
|
14
|
+
else
|
15
|
+
say_status :gemfile, [name, version].compact.join(", ")
|
16
|
+
gemfile.add(name, version: version, group: group, require: require)
|
17
|
+
true
|
18
|
+
end
|
19
|
+
end
|
20
|
+
return if inserted.empty?
|
21
|
+
|
22
|
+
gemfile.save
|
23
|
+
cmd = "install"
|
24
|
+
cmd << " --quiet" if noisy_bundler_version?
|
25
|
+
bundle_command! cmd
|
26
|
+
end
|
27
|
+
alias install_gem install_gems
|
28
|
+
|
29
|
+
def remove_gem(name)
|
30
|
+
gemfile = TidyGemfile.new
|
31
|
+
return unless gemfile.include?(name)
|
32
|
+
|
33
|
+
say_status :gemfile, "remove #{name}"
|
34
|
+
gemfile.remove(name)
|
35
|
+
gemfile.save
|
36
|
+
|
37
|
+
bundle_command! "install", capture: true, verbose: false
|
38
|
+
end
|
39
|
+
|
40
|
+
# Similar to from Rails::Generators::AppBase#bundle_command, but raises if the command fails
|
41
|
+
def bundle_command!(cmd, verbose: true, capture: false)
|
42
|
+
bundler = Gem.bin_path("bundler", "bundle")
|
43
|
+
full_command = %("#{Gem.ruby}" "#{bundler}" #{cmd})
|
44
|
+
|
45
|
+
Bundler.with_original_env do
|
46
|
+
say_status :bundle, cmd, :green if verbose
|
47
|
+
run! full_command, env: {"BUNDLE_IGNORE_MESSAGES" => "1"}, verbose: false, capture: capture
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def bundler_version_satisifed?(spec)
|
52
|
+
require "bundler"
|
53
|
+
Gem::Requirement.new(spec).satisfied_by?(Gem::Version.new(Bundler::VERSION))
|
54
|
+
rescue LoadError
|
55
|
+
false
|
56
|
+
end
|
57
|
+
|
58
|
+
def noisy_bundler_version?
|
59
|
+
bundler_version_satisifed?("< 2.4.17")
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require "open3"
|
2
|
+
require "securerandom"
|
3
|
+
require "shellwords"
|
4
|
+
require "tmpdir"
|
5
|
+
|
6
|
+
module Nextgen
|
7
|
+
module Actions::Git
|
8
|
+
def say_git(message)
|
9
|
+
@commit_messages << message if @commit_messages
|
10
|
+
say message, :cyan
|
11
|
+
end
|
12
|
+
|
13
|
+
def apply_as_git_commit(path, message: nil)
|
14
|
+
say message, :cyan if message
|
15
|
+
@commit_messages = []
|
16
|
+
initially_clean = (git_working? && git_status == :clean)
|
17
|
+
say_status :apply, File.basename(path)
|
18
|
+
apply path, verbose: false
|
19
|
+
return if !initially_clean || git_status == :clean
|
20
|
+
|
21
|
+
commit = message&.dup || "Apply #{File.basename(path)} generator from nextgen"
|
22
|
+
commit << ("\n\n- " + @commit_messages.join("\n- ")) unless @commit_messages.empty?
|
23
|
+
git_commit_all(commit)
|
24
|
+
ensure
|
25
|
+
@commit_messages = nil
|
26
|
+
end
|
27
|
+
|
28
|
+
def git_commit_all(msg)
|
29
|
+
tmp_file = File.join(Dir.tmpdir, "nextgen_git_message_#{SecureRandom.hex(8)}.rb")
|
30
|
+
File.write(tmp_file, msg.rstrip + "\n")
|
31
|
+
|
32
|
+
git add: ".", commit: "-F #{tmp_file.shellescape}"
|
33
|
+
end
|
34
|
+
|
35
|
+
def git_working?
|
36
|
+
return @git_working if defined?(@git_working)
|
37
|
+
|
38
|
+
@git_working = (git_status != :error && git_user_configured?)
|
39
|
+
end
|
40
|
+
|
41
|
+
def git_user_configured?
|
42
|
+
out, status = Open3.capture2e("git config -l")
|
43
|
+
status.success? && out.match?(/^user\.name=/) && out.match?(/^user\.email=/)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Returns :clean, :dirty, or :error
|
47
|
+
def git_status
|
48
|
+
return :error unless Dir.exist?(".git")
|
49
|
+
|
50
|
+
output, status = Open3.capture2e("git status --porcelain")
|
51
|
+
return :error unless status.success?
|
52
|
+
|
53
|
+
output.empty? ? :clean : :dirty
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Nextgen
|
2
|
+
module Actions::Yarn
|
3
|
+
def add_yarn_packages(*packages, dev: false)
|
4
|
+
add = dev ? "add -D" : "add"
|
5
|
+
yarn_command "#{add} #{packages.map(&:shellescape).join(" ")}"
|
6
|
+
end
|
7
|
+
alias add_yarn_package add_yarn_packages
|
8
|
+
|
9
|
+
def remove_yarn_packages(*packages, capture: false)
|
10
|
+
yarn_command "remove #{packages.map(&:shellescape).join(" ")}", capture: capture
|
11
|
+
end
|
12
|
+
alias remove_yarn_package remove_yarn_packages
|
13
|
+
|
14
|
+
def add_package_json_scripts(scripts)
|
15
|
+
scripts.each do |name, script|
|
16
|
+
cmd = "npm pkg set scripts.#{name.to_s.shellescape}=#{script.shellescape}"
|
17
|
+
say_status :run, cmd.truncate(60), :green
|
18
|
+
run! cmd, verbose: false
|
19
|
+
end
|
20
|
+
end
|
21
|
+
alias add_package_json_script add_package_json_scripts
|
22
|
+
|
23
|
+
def yarn_command(cmd, capture: false)
|
24
|
+
say_status :yarn, cmd, :green
|
25
|
+
output = run! "yarn #{cmd}", capture: true, verbose: false
|
26
|
+
return output if capture
|
27
|
+
return puts(output) unless output.match?(/^success /)
|
28
|
+
|
29
|
+
puts output.lines.grep(/^(warning|success) /).join
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,155 @@
|
|
1
|
+
module Nextgen
|
2
|
+
module Actions
|
3
|
+
include Bundler
|
4
|
+
include Git
|
5
|
+
include Yarn
|
6
|
+
|
7
|
+
def with_nextgen_source_path
|
8
|
+
path = Nextgen.template_path.to_s
|
9
|
+
source_paths.unshift(path)
|
10
|
+
yield
|
11
|
+
ensure
|
12
|
+
source_paths.shift if source_paths[0] == path
|
13
|
+
end
|
14
|
+
|
15
|
+
# Like Thor's built-in `run`, but raises a descriptive exception if the command fails.
|
16
|
+
def run!(cmd, env: {}, verbose: true, capture: false)
|
17
|
+
if capture
|
18
|
+
say_status :run, cmd, :green if verbose
|
19
|
+
return if options[:pretend]
|
20
|
+
|
21
|
+
require "open3"
|
22
|
+
result, status = Open3.capture2e(env, cmd)
|
23
|
+
success = status.success?
|
24
|
+
else
|
25
|
+
result = success = run(cmd, env: env, verbose: verbose)
|
26
|
+
end
|
27
|
+
|
28
|
+
return result if success
|
29
|
+
|
30
|
+
say result if result.present?
|
31
|
+
die "Failed to run command. Cannot continue. (#{cmd})"
|
32
|
+
end
|
33
|
+
|
34
|
+
def move(from, to)
|
35
|
+
if File.exist?(to)
|
36
|
+
say_status :skip, "#{to} exists", :yellow
|
37
|
+
return
|
38
|
+
end
|
39
|
+
unless File.exist?(from)
|
40
|
+
say_status :skip, "#{from} does not exit", :yellow
|
41
|
+
return
|
42
|
+
end
|
43
|
+
|
44
|
+
say_status :move, [from, to].join(" → "), :green
|
45
|
+
FileUtils.mv(from, to)
|
46
|
+
end
|
47
|
+
|
48
|
+
def minitest?
|
49
|
+
File.exist?("test/test_helper.rb")
|
50
|
+
end
|
51
|
+
|
52
|
+
def rspec?
|
53
|
+
File.exist?("spec/spec_helper.rb")
|
54
|
+
end
|
55
|
+
|
56
|
+
def copy_test_support_file(file)
|
57
|
+
if minitest?
|
58
|
+
create_test_support_directory
|
59
|
+
copy_file "test/support/#{file}"
|
60
|
+
elsif rspec?
|
61
|
+
empty_directory "spec/support"
|
62
|
+
spec_file_path = "spec/support/#{file}"
|
63
|
+
if Nextgen.template_path.join(spec_file_path).exist?
|
64
|
+
copy_file spec_file_path
|
65
|
+
else
|
66
|
+
copy_file "test/support/#{file}", spec_file_path
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def create_test_support_directory
|
72
|
+
empty_directory "test/support"
|
73
|
+
return if File.exist?("test/test_helper.rb") && File.read("test/test_helper.rb").include?("support/**/*.rb")
|
74
|
+
|
75
|
+
append_to_file "test/test_helper.rb", <<~RUBY
|
76
|
+
|
77
|
+
Dir[File.expand_path("support/**/*.rb", __dir__)].sort.each { |rb| require(rb) }
|
78
|
+
RUBY
|
79
|
+
end
|
80
|
+
|
81
|
+
def document_deploy_var(var_name, desc = nil, required: false, default: nil)
|
82
|
+
insertion = "`#{var_name}`"
|
83
|
+
insertion << " - #{desc}" if desc.present?
|
84
|
+
insertion << " (default: #{default})" unless default.nil?
|
85
|
+
insertion.prepend "**REQUIRED** " if required
|
86
|
+
|
87
|
+
copy_file "DEPLOYMENT.md" unless File.exist?("DEPLOYMENT.md")
|
88
|
+
inject_into_file "DEPLOYMENT.md", "#{insertion}\n- ", after: /^## Environment variables.*?^- /m
|
89
|
+
end
|
90
|
+
|
91
|
+
def add_lint_task(task, fix: "#{task}:autocorrect")
|
92
|
+
unless File.read("README.md").include?(" and lint checks")
|
93
|
+
inject_into_file "README.md", " and lint checks", after: "automated tests"
|
94
|
+
inject_into_file "README.md", <<~MARKDOWN, after: "```\nbin/rake\n```\n"
|
95
|
+
|
96
|
+
> [!NOTE]
|
97
|
+
> Rake allows you to run all checks in parallel with the `-m` option. This is much faster, but since the output is interleaved, it may be harder to read.
|
98
|
+
|
99
|
+
```
|
100
|
+
bin/rake -m
|
101
|
+
```
|
102
|
+
|
103
|
+
### Fixing lint issues
|
104
|
+
|
105
|
+
Some lint issues can be auto-corrected. To fix them, run:
|
106
|
+
|
107
|
+
```
|
108
|
+
bin/rake fix
|
109
|
+
```
|
110
|
+
MARKDOWN
|
111
|
+
end
|
112
|
+
|
113
|
+
unless File.read("Rakefile").include?("task fix:")
|
114
|
+
append_to_file "Rakefile", <<~RUBY
|
115
|
+
|
116
|
+
desc "Apply auto-corrections"
|
117
|
+
task fix: %w[] do
|
118
|
+
Thor::Base.shell.new.say_status :OK, "All fixes applied!"
|
119
|
+
end
|
120
|
+
RUBY
|
121
|
+
end
|
122
|
+
|
123
|
+
inject_into_file "Rakefile", " #{task}", after: /task default: %w\[[^\]]*/
|
124
|
+
inject_into_file "Rakefile", " #{fix}", after: /task fix: %w\[[^\]]*/
|
125
|
+
end
|
126
|
+
|
127
|
+
def gitignore(*lines)
|
128
|
+
return unless File.exist?(".gitignore")
|
129
|
+
|
130
|
+
lines -= File.read(".gitignore").lines(chomp: true)
|
131
|
+
return if lines.empty?
|
132
|
+
|
133
|
+
text = lines.map(&:strip).join("\n")
|
134
|
+
append_to_file ".gitignore", "#{text}\n"
|
135
|
+
end
|
136
|
+
|
137
|
+
def prevent_autoload_lib(*paths)
|
138
|
+
return unless File.read("config/application.rb").match?(/^\s*config.autoload_lib/)
|
139
|
+
|
140
|
+
paths.reverse_each do |path|
|
141
|
+
gsub_file("config/application.rb", /autoload_lib\(ignore: %w.*$/) do |match|
|
142
|
+
next match if match.match?(/%w.*[\(\[ ]#{Regexp.quote(path)}[ \)\]]/)
|
143
|
+
|
144
|
+
match.sub(/%w[\(\[]/, '\0' + path + " ")
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def die(message = nil)
|
150
|
+
message = message.sub(/^/, "ERROR: ") if message && !message.start_with?(/error/i)
|
151
|
+
|
152
|
+
raise Thor::Error, message
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
data/lib/nextgen/cli.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require "thor"
|
2
|
+
|
3
|
+
module Nextgen
|
4
|
+
class CLI < Thor
|
5
|
+
extend ThorExtensions
|
6
|
+
|
7
|
+
map %w[-v --version] => "version"
|
8
|
+
|
9
|
+
desc "create APP_PATH", "Generate a Rails app interactively in APP_PATH"
|
10
|
+
def create(app_path)
|
11
|
+
Commands::Create.run(app_path, options)
|
12
|
+
end
|
13
|
+
|
14
|
+
desc "version", "Display nextgen version", hide: true
|
15
|
+
def version
|
16
|
+
say "nextgen/#{VERSION} #{RUBY_DESCRIPTION}"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|