Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 124 additions & 0 deletions exe/perron
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
#!/usr/bin/env ruby

require "bundler/setup"

RAILS_BIN = File.expand_path("bin/rails", Dir.pwd)

USAGE = <<~USAGE
Usage: perron <command> [options]

Commands:
perron new APPNAME Create a new Perron-powered Rails app
perron generate NAME Scaffold a new content type (actions: index show)
perron validate Validate all content resources
perron sync_sources Sync all source-backed resources (pass NAME to sync specific)
perron build Build static site (defaults to RAILS_ENV=production)
perron clobber Remove built output
perron deploy Deploy static site

Examples:
perron new my-site
perron generate Post show index
perron destroy Post
perron sync_sources Tool
USAGE

def ensure_rails!
return if File.exist?(RAILS_BIN)

warn "Error: bin/rails not found in #{Dir.pwd}"
warn "Make sure you are running from your Perron project directory."

exit 1
end

def new_command
app_name = ARGV.shift

unless app_name
warn "Error: app name required"

puts USAGE

exit 1
end

ENV["LOCATION"] = File.expand_path("lib/perron/install/template.rb", Dir.pwd)

exec RAILS_BIN, "app:template"
end

def generate_command
ensure_rails!

exec RAILS_BIN, "generate", "content", *ARGV
end

def destroy_command
ensure_rails!

exec RAILS_BIN, "destroy", "content", *ARGV
end

def validate_command
ensure_rails!

exec RAILS_BIN, "perron:validate"
end

def sync_sources_command
ensure_rails!

exec RAILS_BIN, "perron:sync_sources", *ARGV
end

def build_command
ensure_rails!

ENV["RAILS_ENV"] ||= "production"

exec RAILS_BIN, "perron:build"
end

def clobber_command
ensure_rails!

exec RAILS_BIN, "perron:clobber"
end

def deploy_command
ensure_rails!

exec RAILS_BIN, "perron:deploy"
end

if ARGV.empty?
puts USAGE

exit 0
end

command = ARGV.shift

if command == "generate" && (ARGV.include?("-h") || ARGV.include?("--help"))
ensure_rails!

exec RAILS_BIN, "generate", "content", "-h"
end

case command
when "new" then new_command
when "generate" then generate_command
when "destroy" then destroy_command
when "validate" then validate_command
when "sync_sources" then sync_sources_command
when "build" then build_command
when "clobber" then clobber_command
when "deploy" then deploy_command
else
warn "Unknown command: #{command}"

puts USAGE

exit 1
end
5 changes: 5 additions & 0 deletions lib/perron/install.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@
say "Create Perron initializer"
copy_file "#{__dir__}/install/initializer.rb", "config/initializers/perron.rb"

say "Create perron binstub"
copy_file "#{__dir__}/install/perron.tt", "bin/perron", force: true

run "chmod +x bin/perron"

say "Create content data directory"
copy_file "#{__dir__}/install/README.md", "app/content/data/README.md"

Expand Down
2 changes: 1 addition & 1 deletion lib/perron/install/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@
# site_id: your_site_id

before_actions:
- RAILS_ENV=production bundle exec rake perron:build
- bundle exec perron build
after_actions:
- bundle exec rake perron:clobber
2 changes: 2 additions & 0 deletions lib/perron/install/perron.tt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/usr/bin/env ruby
exec "bundle", "exec", "perron", *ARGV
87 changes: 87 additions & 0 deletions lib/perron/install/template.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
gem "perron" unless File.read("Gemfile").include?("perron")

after_bundle do
unless File.exist?("config/initializers/perron.rb")
rails_command "perron:install"
end

gsub_file "Gemfile", /gem "sqlite3".*$/, ""
gsub_file "Gemfile", /gem "activerecord".*$/, ""

say "Remove files"
remove_file "config/database.yml"
remove_file "config/credentials.yml.enc"
remove_file "config/master.key"
remove_file "public/400.html"
remove_file "public/406-unsupported-browser.html"
remove_file "public/422.html"
remove_file "public/500.html"
remove_file "app/controllers/application_controller.rb"
remove_file "app/views/layouts/application.html.erb"
remove_file "README.md"

run "rm -r app/views/pwa" if File.directory?("app/views/pwa")

append_to_file ".gitignore", "/output/\n" if File.exist?(".gitignore")

say "Create application files"
create_file "app/controllers/application_controller.rb", <<~RB
class ApplicationController < ActionController::Base
end
RB

create_file "app/views/layouts/application.html.erb", <<~ERB
<!DOCTYPE html>
<html lang="en">
<head>
<%= meta_tags %>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">

<%= yield :head %>

<link rel="icon" href="/icon.png" type="image/png">
<link rel="icon" href="/icon.svg" type="image/svg+xml">
<link rel="apple-touch-icon" href="/icon.png">

<%= stylesheet_link_tag :app %>
<!-- Learn more on https://attractivejs.railsdesigner.com/ -->
<script defer src="https://cdn.jsdelivr.net/npm/attractivejs@latest"></script>
</head>
<body>
<%= yield %>
</body>
</html>
ERB

create_file "README.md", <<~MARKDOWN
# #{app_name.titleize}

TBD


## Development

```
bin/dev
```


## Deploy/publish

```
bin/perron build
```
MARKDOWN

markdown_gem = ask("Which markdown parser would you like to use? (commonmarker/kramdown/redcarpet or leave blank to skip):")

gem_name = markdown_gem.strip.downcase
if %w[commonmarker kramdown redcarpet].include?(gem_name)
uncomment_lines "Gemfile", /gem "#{gem_name}"/

Bundler.with_unbundled_env { run "bundle install" }
elsif markdown_gem.present?
say "Invalid markdown parser option. Skipping…", :yellow
end
end
2 changes: 1 addition & 1 deletion lib/perron/tasks/build.rake
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ namespace :perron do
task build: :environment do
unless Rails.env.production?
puts "⚠️ Not running in production mode. Unpublished content will be included in the build."
puts " └─> Run in production mode using: RAILS_ENV=production bin/rails perron:build"
puts " └─> Run in production mode using: bin/perron build"
puts ""
end

Expand Down
2 changes: 2 additions & 0 deletions perron.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ Gem::Specification.new do |spec|
spec.metadata["source_code_uri"] = "https://github.com/Rails-Designer/perron/"

spec.files = Dir["{bin,app,lib}/**/*", "Rakefile", "README.md", "perron.gemspec", "Gemfile", "Gemfile.lock"]
spec.bindir = "exe"
spec.executables = ["perron"]

spec.required_ruby_version = ">= 3.4.0"

Expand Down
58 changes: 58 additions & 0 deletions test/perron/cli_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
require "test_helper"
require "open3"

class CliTest < ActiveSupport::TestCase
EXE_PATH = File.expand_path("../../exe/perron", __dir__)

test "shows help without args" do
stdout, stderr, status = Open3.capture3("ruby", EXE_PATH)

assert_match(/Usage: perron <command>/, stdout + stderr)
assert_match(/perron new APPNAME/, stdout + stderr)
assert_match(/perron generate/, stdout + stderr)
assert_match(/perron build/, stdout + stderr)
assert_match(/perron clobber/, stdout + stderr)
assert_match(/perron deploy/, stdout + stderr)
end

test "shows help with --help" do
stdout, stderr, status = Open3.capture3("ruby", EXE_PATH, "--help")

assert_match(/Usage: perron <command>/, stdout + stderr)
end

test "shows help with -h" do
stdout, stderr, status = Open3.capture3("ruby", EXE_PATH, "-h")

assert_match(/Usage: perron <command>/, stdout + stderr)
end

test "errors on unknown command" do
stdout, stderr, status = Open3.capture3("ruby", EXE_PATH, "unknown")

assert_equal 1, status.exitstatus
assert_match(/Unknown command: unknown/, stdout + stderr)
end

test "errors on new without app name" do
Dir.mktmpdir do |dir|
Dir.chdir(dir) do
stdout, stderr, status = Open3.capture3("ruby", EXE_PATH, "new")

assert_equal 1, status.exitstatus
assert_match(/Error: app name required/, stdout + stderr)
end
end
end

test "errors when bin/rails not found" do
Dir.mktmpdir do |dir|
Dir.chdir(dir) do
stdout, stderr, status = Open3.capture3("ruby", EXE_PATH, "build")

assert_equal 1, status.exitstatus
assert_match(/bin\/rails not found/, stdout + stderr)
end
end
end
end
9 changes: 9 additions & 0 deletions test/perron/install_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ def setup
FileUtils.mkdir_p(@tmpdir)
FileUtils.mkdir_p("#{@tmpdir}/config/initializers")
FileUtils.mkdir_p("#{@tmpdir}/app/content/data")
FileUtils.mkdir_p("#{@tmpdir}/bin")

File.write("#{@tmpdir}/Gemfile", "source 'https://rubygems.org'\n")
File.write("#{@tmpdir}/.gitignore", "")
Expand All @@ -20,6 +21,12 @@ def test_creates_perron_initializer
assert_match(/Perron.configure do |config|/, File.read("#{@tmpdir}/config/initializers/perron.rb"))
end

def test_creates_perron_binstub
run_template

assert File.exist?("#{@tmpdir}/bin/perron")
end

def test_creates_data_folder_with_readme
run_template

Expand Down Expand Up @@ -52,6 +59,8 @@ def run_template
Dir.chdir(@tmpdir) do
FileUtils.cp("#{@install_dir}/initializer.rb.tt", 'config/initializers/perron.rb')
FileUtils.cp("#{@install_dir}/README.md.tt", 'app/content/data/README.md')
FileUtils.cp("#{@install_dir}/perron.tt", 'bin/perron')
FileUtils.chmod("+x", 'bin/perron')
File.open("Gemfile", "a") do |file|
file.write <<~RUBY

Expand Down
2 changes: 1 addition & 1 deletion test/perron/tasks/build_rake_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class BuildTest < ActiveSupport::TestCase
end
end.first

assert_match "RAILS_ENV=production bin/rails perron:build", output
assert_match "bin/perron build", output
end

test "does not warn when running in production" do
Expand Down
Loading