Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add scripts to help with project maintenance #577

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
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
58 changes: 58 additions & 0 deletions bin/build
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#!/usr/bin/env ruby

require "fileutils"
require "yaml"
require_relative "lib/build_config"
require_relative "lib/builder"
require_relative "lib/terminal"

# Usage:
#
# $ ./build "all_variants" | "vanilla" | "default" | <ci_config_name>
#
# where <ci_config_name> is the name of a file in the `ci/configs` directory
# (minus the .yml extension)
#
# Examples:
#
# $ ./build all_variants # build every known variant
# $ ./build vanilla # build a vanilla Rails app (which doesn't use our template)
# $ ./build default # build using the default config
# $ ./build basic # build using the basic CI config
# $ ./build react # build using the React CI config
#
# You can capture the output to a file using the `tee` command:
#
# $ ./build all_variants | tee build.log
#
# If you want to capture the output to a file and keep the color output, you can
# use the `unbuffer` command:
#
# $ brew install expect # or your linux distro equivalent
# $ unbuffer ./build all_variants | tee build.log

class Main
class << self
def main
configs = BuildConfig.resolve_to_configs(ARGV.first)

Terminal.puts_header(configs_summary(configs))

configs.each do |config|
Builder.new(config:).build
end
end

private

def configs_summary(configs)
<<~EO_HEAD
Building apps for #{configs.length} configs:
- #{configs.map(&:name).join("\n - ")}

EO_HEAD
end
end
end

Main.main
20 changes: 20 additions & 0 deletions bin/create_comarison_repo_prs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/usr/bin/env ruby

require_relative "lib/comparison_config"
require_relative "lib/terminal"

class Main
class << self
def main(dry_run:)
Dir.chdir(ComparisonConfig.comparison_repo_root_path) do
ComparisonConfig.comparison_builds.each do |build_name|
Terminal.puts_header("Creating PR for #{build_name}")
cmd = "gh pr create --title 'Compare #{build_name} against vanilla Rails' --body 'Compare #{build_name} against vanilla Rails' --base main --head #{build_name} #{dry_run ? "--dry-run" : ""}"
system(cmd)
end
end
end
end
end

Main.main(dry_run: ARGV.include?("--dry-run"))
87 changes: 87 additions & 0 deletions bin/create_comparison_repo
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
#!/usr/bin/env ruby

require "fileutils"
require_relative "lib/comparison_config"
require_relative "lib/terminal"

class Main
class << self
def main
reset_comparison_repo
use_vanilla_rails_as_main_branch

ComparisonConfig.comparison_builds.each do |build_name|
populate_new_branch_for_build(build_name:)
checkout_main
end
end

private

def reset_comparison_repo
repo_path = ComparisonConfig.comparison_repo_root_path

FileUtils.rm_rf(repo_path)
FileUtils.mkdir_p(repo_path)

Dir.chdir(repo_path) do
system("git init")
system("git remote add origin [email protected]:ackama/rails-template-variants-comparison.git")
end
end

def use_vanilla_rails_as_main_branch
Terminal.puts_header("Adding vanilla Rails build to main branch")
copy_build(build_path: ComparisonConfig.vanilla_build_path)
commit_changes(commit_message: "Add #{ComparisonConfig.vanilla_build_name}")
end

def populate_new_branch_for_build(build_name:)
Terminal.puts_header("Creating branch for #{build_name}")

rm_rf_except_git_dir
create_new_branch(branch_name: build_name)

build_path = File.join(ComparisonConfig.build_path, build_name)
copy_build(build_path:)

commit_changes(commit_message: "Add #{build_name}")
end

def checkout_main
Dir.chdir(ComparisonConfig.comparison_repo_root_path) do
system("git checkout main")
end
end

# clean out all the files from the previous build (except .git)
def rm_rf_except_git_dir
Dir.chdir(ComparisonConfig.comparison_repo_root_path) do
keepers = [".git", "."]
deletable_files = Dir.glob("{*,.*}").reject { |f| keepers.include?(f) }

FileUtils.rm_rf(deletable_files)
end
end

def create_new_branch(branch_name:)
Dir.chdir(ComparisonConfig.comparison_repo_root_path) do
system("git checkout -b #{branch_name}")
end
end

def commit_changes(commit_message:)
Dir.chdir(ComparisonConfig.comparison_repo_root_path) do
system("git add .")
# skip pre-commit hooks because they're not relevant here
system("git commit --quiet --no-verify -m '#{commit_message}'")
end
end

def copy_build(build_path:)
system("rsync -qav --exclude='.git' --exclude='node_modules' --exclude='tmp' #{build_path}/ #{ComparisonConfig.comparison_repo_root_path}/")
end
end
end

Main.main
96 changes: 96 additions & 0 deletions bin/lib/build_config.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
class BuildConfig
PROJECT_ROOT_PATH = File.absolute_path(File.join(__dir__, "../.."))
CI_CONFIGS_PATH = File.join(PROJECT_ROOT_PATH, "ci/configs")
BUILD_PATH = File.join(PROJECT_ROOT_PATH, "tmp/builds")
TEMPLATE_PATH = File.join(PROJECT_ROOT_PATH, "template.rb")
TARGET_VERSIONS_PATH = File.join(PROJECT_ROOT_PATH, "target_versions.yml")
DEFAULT_CONFIG_NAME = "readme_example".freeze
VANILLA_CONFIG_NAME = "vanilla".freeze
APP_NAME_SUFFIX = "template_app".freeze
CI_CONFIGS = Dir.children(CI_CONFIGS_PATH).map { |f| f.sub(".yml", "") }
AVAILABLE_CONFIG_NAMES = [
VANILLA_CONFIG_NAME,
DEFAULT_CONFIG_NAME,
*CI_CONFIGS
].freeze
TARGET_RAILS_MAJOR_MINOR = YAML.safe_load_file(TARGET_VERSIONS_PATH).fetch("target_rails_major_minor")

def self.all_configs
AVAILABLE_CONFIG_NAMES.map { |name| new(name:) }
end

##
# Resolves the CLI parameter to a list of Config instances
#
def self.resolve_to_configs(cli_param)
return all_configs if cli_param == "all_variants"
return [new(name: cli_param)] if AVAILABLE_CONFIG_NAMES.include?(cli_param)

[new(name: DEFAULT_CONFIG_NAME)]
end

attr_reader :name, :app_name, :config_path

def initialize(name:)
@name = name
@app_name = case name
when VANILLA_CONFIG_NAME
"#{name}_#{target_rails_major}_#{APP_NAME_SUFFIX}"
else
"#{name}_#{APP_NAME_SUFFIX}"
end

@config_path = case name
when DEFAULT_CONFIG_NAME
File.join(PROJECT_ROOT_PATH, "ackama_rails_template.config.yml")
when VANILLA_CONFIG_NAME
nil
else
File.join(CI_CONFIGS_PATH, "#{name}.yml")
end
end

def vanilla?
name == VANILLA_CONFIG_NAME
end

def app_path
File.join(BUILD_PATH, app_name)
end

def target_versions_path
TARGET_VERSIONS_PATH
end

def build_path
BUILD_PATH
end

def template_path
return nil if vanilla?

TEMPLATE_PATH
end

def inspect
<<~INSPECT
<Config:
@name: #{name}
@app_name: #{app_name}
@config_path: #{config_path}
@app_path: #{app_path}
@target_versions_path: #{target_versions_path}
@build_path: #{build_path}
@template_path: #{template_path}
>
INSPECT
end

def target_rails_major_minor
TARGET_RAILS_MAJOR_MINOR
end

def target_rails_major
TARGET_RAILS_MAJOR_MINOR.split(".").first
end
end
102 changes: 102 additions & 0 deletions bin/lib/builder.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
require "rubygems"

class Builder
def initialize(config:)
@config = config
@rails_cmd_version = build_rails_cmd_version(target_rails_major_minor: config.target_rails_major_minor)
end

def build
Dir.chdir(@config.build_path) do
delete_any_previous_build
drop_dbs_from_previous_build

if @config.vanilla?
Terminal.puts_header("Building vanilla Rails app")
system(build_vanilla_cmd_env, build_vanilla_cmd)
else
Terminal.puts_header("Building Rails app")
system(build_cmd_env, build_cmd)
end
end
end

private

def base_cmd_parts
[
"rails _#{@rails_cmd_version}_ new #{@config.app_name}",
"-d postgresql",
"--skip-javascript",
"--skip-kamal",
"--skip-solid"
]
end

def build_rails_cmd_version(target_rails_major_minor:)
specs = Gem::Specification.find_all_by_name("rails")

raise "No versions of gem '#{gem_name}' are installed" if specs.empty?

version = specs
.map { _1.version.to_s }
.sort
.reverse
.find { |v| v.start_with?(target_rails_major_minor) }
test_cmd = "rails _#{version}_ -v"
expected_test_output = "Rails #{version}"
actual_test_output = `#{test_cmd}`.strip

raise "Command failed: #{test_cmd}. Actual: #{actual_test_output}" unless expected_test_output == actual_test_output

Terminal.puts_header("Using Rails version #{version}")

version
end

def delete_any_previous_build
FileUtils.rm_rf(@config.app_path)
end

def build_cmd
cmd_parts = base_cmd_parts.append("-m #{@config.template_path}")

puts <<~EO_INFO
Building command:: #{cmd_parts.inspect}
EO_INFO

cmd_parts.join(" ")
end

def build_cmd_env
cmd_env = {
"TARGET_VERSIONS_PATH" => @config.target_versions_path,
"CONFIG_PATH" => @config.config_path
}

puts <<~EO_INFO
Building ENV: #{cmd_env.inspect}
EO_INFO

cmd_env
end

def build_vanilla_cmd_env
{}
end

def build_vanilla_cmd
puts <<~EO_INFO
Building command: #{base_cmd_parts.inspect}
EO_INFO

base_cmd_parts.join(" ")
end

def drop_dbs_from_previous_build
Terminal.puts_header("Dropping databases from previous build")
system "psql -c 'DROP DATABASE IF EXISTS #{@config.app_name}_test;'"
system "psql -c 'DROP DATABASE IF EXISTS #{@config.app_name}_development;'"
system "psql -c 'DROP DATABASE IF EXISTS #{@config.app_name}_test;'"
end
end
30 changes: 30 additions & 0 deletions bin/lib/comparison_config.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
class ComparisonConfig
PROJECT_ROOT_PATH = File.absolute_path(File.join(__dir__, "../.."))
BUILD_PATH = File.join(PROJECT_ROOT_PATH, "tmp/builds")

class << self
def build_path
BUILD_PATH
end

def comparison_repo_root_path
File.join(PROJECT_ROOT_PATH, "tmp/rails-template-variants-comparison")
end

def vanilla_build_name
all_builds.find { |build_name| build_name.start_with?("vanilla_") }
end

def vanilla_build_path
File.join(BUILD_PATH, vanilla_build_name)
end

def comparison_builds
all_builds - [vanilla_build_name]
end

def all_builds
Dir.children(BUILD_PATH)
end
end
end
Loading
Loading