Skip to content

Commit 4d7583d

Browse files
committed
Add scripts to help with project maintenance
1 parent c0e3fe5 commit 4d7583d

File tree

5 files changed

+324
-0
lines changed

5 files changed

+324
-0
lines changed

bin/build

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#!/usr/bin/env ruby
2+
3+
require "fileutils"
4+
require "yaml"
5+
require_relative "./lib/config"
6+
require_relative "./lib/builder"
7+
8+
# Usage:
9+
#
10+
# $ ./build "all_variants" | "vanilla" | "default" | <ci_config_name>
11+
#
12+
# where <ci_config_name> is the name of a file in the `ci/configs` directory
13+
# (minus the .yml extension)
14+
#
15+
# Examples:
16+
#
17+
# $ ./build all_variants # build every known variant
18+
# $ ./build vanilla # build a vanilla Rails app (which doesn't use our template)
19+
# $ ./build default # build using the default config
20+
# $ ./build basic # build using the basic CI config
21+
# $ ./build react # build using the React CI config
22+
23+
class Main
24+
class << self
25+
def main
26+
configs = Config.resolve_to_configs(ARGV.first)
27+
28+
puts configs_summary(configs)
29+
30+
configs.each do |config|
31+
Builder.new(config:).build
32+
end
33+
end
34+
35+
private
36+
37+
def configs_summary(configs)
38+
<<~EO_HEAD
39+
Building apps for #{configs.length} configs:
40+
- #{configs.map(&:name).join("\n - ")}
41+
42+
EO_HEAD
43+
end
44+
end
45+
end
46+
47+
Main.main

bin/compare

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
#!/usr/bin/env ruby
2+
3+
require "fileutils"
4+
require "pry"
5+
6+
PROJECT_ROOT_PATH = File.absolute_path(File.join(__dir__, "."))
7+
BUILD_PATH = File.join(PROJECT_ROOT_PATH, "tmp/builds")
8+
COMPARISONS_REPO_ROOT_PATH = File.join(PROJECT_ROOT_PATH, "tmp/rails-template-variants-comparison")
9+
ALL_BUILDS = Dir.children(BUILD_PATH)
10+
11+
VANILLA_BUILD_NAME = ALL_BUILDS.find { |build_name| build_name.start_with?("vanilla_") }
12+
VANILLA_BUILD_PATH = File.join(BUILD_PATH, VANILLA_BUILD_NAME)
13+
COMPARISON_BUILDS = ALL_BUILDS - [VANILLA_BUILD_NAME]
14+
15+
def copy_build(build_name:, branch_name:, build_path:)
16+
system("git checkout -b #{branch_name}")
17+
18+
# the `.` at the end of the cp command is what copies the contents of the dir
19+
# including "hidden" `.` files (the same trick works with shell `cp` command)
20+
# FileUtils.cp_r("#{build_path}/.", COMPARISONS_REPO_ROOT_PATH)
21+
system("rsync -av --exclude='.git' --exclude='node_modules' --exclude='tmp' #{build_path}/ #{COMPARISONS_REPO_ROOT_PATH}/")
22+
23+
system("git add .")
24+
system("git commit -n -m 'Add #{build_name}'")
25+
end
26+
27+
def main
28+
FileUtils.rm_rf(COMPARISONS_REPO_ROOT_PATH)
29+
FileUtils.mkdir_p(COMPARISONS_REPO_ROOT_PATH)
30+
31+
Dir.chdir(COMPARISONS_REPO_ROOT_PATH) do
32+
system("git init")
33+
system("git remote add origin [email protected]:ackama/rails-template-variants-comparison.git")
34+
35+
copy_build(
36+
build_name: VANILLA_BUILD_NAME,
37+
branch_name: "main",
38+
build_path: VANILLA_BUILD_PATH
39+
)
40+
41+
COMPARISON_BUILDS.each do |build_name|
42+
build_path = File.join(BUILD_PATH, build_name)
43+
keepers = [".git", "."]
44+
deletable_files = Dir.glob("{*,.*}").reject { |f| keepers.include?(f) }
45+
46+
# clean out all the files from the previous build (except .git)
47+
FileUtils.rm_rf(deletable_files)
48+
49+
copy_build(
50+
build_name:,
51+
branch_name: build_name,
52+
build_path:
53+
)
54+
55+
# go back to main before we start the next build
56+
system("git checkout main")
57+
end
58+
end
59+
end
60+
61+
main

bin/create_prs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#!/usr/bin/env ruby
2+
3+
PROJECT_ROOT_PATH = File.absolute_path(File.join(__dir__, "."))
4+
BUILD_PATH = File.join(PROJECT_ROOT_PATH, "tmp/builds")
5+
COMPARISONS_REPO_ROOT_PATH = File.join(PROJECT_ROOT_PATH, "tmp/rails-template-variants-comparison")
6+
ALL_BUILDS = Dir.children(BUILD_PATH)
7+
VANILLA_BUILD_NAME = ALL_BUILDS.find { |build_name| build_name.start_with?("vanilla_") }
8+
COMPARISON_BUILDS = ALL_BUILDS - [VANILLA_BUILD_NAME]
9+
10+
def main
11+
Dir.chdir(COMPARISONS_REPO_ROOT_PATH) do
12+
COMPARISON_BUILDS.each do |build_name|
13+
puts build_name
14+
# add --dry-run to test
15+
system("gh pr create --title 'Compare #{build_name} against vanilla Rails' --body 'Compare #{build_name} against vanilla Rails' --base main --head #{build_name}")
16+
end
17+
end
18+
end
19+
20+
main

bin/lib/builder.rb

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
##
2+
# Invokes the template as a user would, not as CI does via the build-and-test script.
3+
# Automatically removes old DB before generating the app if required
4+
#
5+
class Builder
6+
def initialize(config:)
7+
@config = config
8+
end
9+
10+
def build
11+
verify_rails_cmd_is_expected_version!
12+
13+
Dir.chdir(@config.build_path) do
14+
delete_any_previous_build
15+
drop_dbs_from_previous_build
16+
17+
# TODO: how to handle errors? raise? or log and continue? or rename the app dir with a .failed suffix?
18+
# TODO: how to handle output? redirect to a file? or sep files for stdout and stderr?
19+
20+
if @config.vanilla?
21+
system(build_vanilla_cmd_env, build_vanilla_cmd)
22+
else
23+
system(build_cmd_env, build_cmd)
24+
end
25+
end
26+
end
27+
28+
private
29+
30+
def base_cmd_parts
31+
[
32+
"rails new #{@config.app_name}",
33+
"-d postgresql",
34+
"--skip-javascript",
35+
"--skip-kamal",
36+
"--skip-solid"
37+
]
38+
end
39+
40+
def verify_rails_cmd_is_expected_version!
41+
actual_rails_version = `rails -v`.strip.sub("Rails ", "")
42+
43+
return if actual_rails_version.start_with?(@config.target_rails_major_minor)
44+
45+
raise "Expected Rails version #{@config.target_rails_major_minor}, but got #{actual_rails_version}"
46+
end
47+
48+
def delete_any_previous_build
49+
FileUtils.rm_rf(@config.app_path)
50+
end
51+
52+
def build_cmd
53+
cmd_parts = base_cmd_parts.append("-m #{@config.template_path}")
54+
55+
puts <<~EO_INFO
56+
Building command:: #{cmd_parts.inspect}
57+
EO_INFO
58+
59+
cmd_parts.join(" ")
60+
end
61+
62+
def build_cmd_env
63+
cmd_env = {
64+
"TARGET_VERSIONS_PATH" => @config.target_versions_path,
65+
"CONFIG_PATH" => @config.config_path
66+
}
67+
68+
puts <<~EO_INFO
69+
Building ENV: #{cmd_env.inspect}
70+
EO_INFO
71+
72+
cmd_env
73+
end
74+
75+
def build_vanilla_cmd_env
76+
{}
77+
end
78+
79+
def build_vanilla_cmd
80+
puts <<~EO_INFO
81+
Building command: #{base_cmd_parts.inspect}
82+
EO_INFO
83+
84+
base_cmd_parts.join(" ")
85+
end
86+
87+
def print_separator
88+
puts ""
89+
puts "*" * 80
90+
puts ""
91+
end
92+
93+
def drop_dbs_from_previous_build
94+
print_separator
95+
system "psql -c 'DROP DATABASE IF EXISTS #{@config.app_name}_test;'"
96+
system "psql -c 'DROP DATABASE IF EXISTS #{@config.app_name}_development;'"
97+
system "psql -c 'DROP DATABASE IF EXISTS #{@config.app_name}_test;'"
98+
print_separator
99+
end
100+
end

bin/lib/config.rb

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
class Config
2+
PROJECT_ROOT_PATH = File.absolute_path(File.join(__dir__, "../.."))
3+
CI_CONFIGS_PATH = File.join(PROJECT_ROOT_PATH, "ci/configs")
4+
BUILD_PATH = File.join(PROJECT_ROOT_PATH, "tmp/builds")
5+
TEMPLATE_PATH = File.join(PROJECT_ROOT_PATH, "template.rb")
6+
TARGET_VERSIONS_PATH = File.join(PROJECT_ROOT_PATH, "target_versions.yml")
7+
DEFAULT_CONFIG_NAME = "readme_example".freeze
8+
VANILLA_CONFIG_NAME = "vanilla".freeze
9+
APP_NAME_SUFFIX = "template_app".freeze
10+
CI_CONFIGS = Dir.children(CI_CONFIGS_PATH).map { |f| f.sub(".yml", "") }
11+
AVAILABLE_CONFIG_NAMES = [
12+
VANILLA_CONFIG_NAME,
13+
DEFAULT_CONFIG_NAME,
14+
*CI_CONFIGS
15+
].freeze
16+
TARGET_RAILS_MAJOR_MINOR = YAML.safe_load_file(TARGET_VERSIONS_PATH).fetch("target_rails_major_minor")
17+
18+
def self.all_configs
19+
AVAILABLE_CONFIG_NAMES.map { |name| new(name:) }
20+
end
21+
22+
##
23+
# Resolves the CLI parameter to a list of Config instances
24+
#
25+
def self.resolve_to_configs(cli_param)
26+
return all_configs if cli_param == "all_variants"
27+
return [new(name: cli_param)] if AVAILABLE_CONFIG_NAMES.include?(cli_param)
28+
29+
[new(name: DEFAULT_CONFIG_NAME)]
30+
end
31+
32+
attr_reader :name, :app_name, :config_path
33+
34+
def initialize(name:)
35+
@name = name
36+
@app_name = case name
37+
when VANILLA_CONFIG_NAME
38+
"#{name}_#{target_rails_major}_#{APP_NAME_SUFFIX}"
39+
else
40+
"#{name}_#{APP_NAME_SUFFIX}"
41+
end
42+
43+
@config_path = case name
44+
when DEFAULT_CONFIG_NAME
45+
File.join(PROJECT_ROOT_PATH, "ackama_rails_template.config.yml")
46+
when VANILLA_CONFIG_NAME
47+
nil
48+
else
49+
File.join(CI_CONFIGS_PATH, "#{name}.yml")
50+
end
51+
end
52+
53+
def vanilla?
54+
name == VANILLA_CONFIG_NAME
55+
end
56+
57+
def app_path
58+
File.join(BUILD_PATH, app_name)
59+
end
60+
61+
def target_versions_path
62+
TARGET_VERSIONS_PATH
63+
end
64+
65+
def build_path
66+
BUILD_PATH
67+
end
68+
69+
def template_path
70+
return nil if vanilla?
71+
72+
TEMPLATE_PATH
73+
end
74+
75+
def inspect
76+
<<~INSPECT
77+
<Config:
78+
@name: #{name}
79+
@app_name: #{app_name}
80+
@config_path: #{config_path}
81+
@app_path: #{app_path}
82+
@target_versions_path: #{target_versions_path}
83+
@build_path: #{build_path}
84+
@template_path: #{template_path}
85+
>
86+
INSPECT
87+
end
88+
89+
def target_rails_major_minor
90+
TARGET_RAILS_MAJOR_MINOR
91+
end
92+
93+
def target_rails_major
94+
TARGET_RAILS_MAJOR_MINOR.split(".").first
95+
end
96+
end

0 commit comments

Comments
 (0)