Created
May 29, 2025 05:21
-
-
Save SamSaffron/95ad64dfc921174cb2cbadfcbbed66e8 to your computer and use it in GitHub Desktop.
rename_script
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env ruby | |
# frozen_string_literal: true | |
require "fileutils" | |
require "date" | |
class PersonaToAgentRenamer | |
def initialize | |
@plugin_root = Dir.pwd | |
@manifest = [] | |
end | |
def run | |
case ARGV[0] | |
when "--content-only" | |
validate_directory | |
replace_content_in_files | |
print_content_summary | |
when "--migrations-only" | |
validate_directory | |
create_migration | |
create_post_migration | |
print_migration_summary | |
else | |
puts colorize("=" * 55, :blue) | |
puts colorize("Renaming 'persona' to 'agent' throughout the codebase", :blue) | |
puts colorize("=" * 55, :blue) | |
puts colorize("Working in: #{@plugin_root}", :blue) | |
validate_directory | |
replace_content_in_files | |
rename_files_and_directories | |
create_migration | |
create_post_migration | |
print_summary | |
end | |
end | |
private | |
def colorize(text, color) | |
colors = { | |
red: "\033[0;31m", | |
green: "\033[0;32m", | |
yellow: "\033[0;33m", | |
blue: "\033[0;34m", | |
nc: "\033[0m", | |
} | |
"#{colors[color]}#{text}#{colors[:nc]}" | |
end | |
def validate_directory | |
unless File.exist?(File.join(@plugin_root, "plugin.rb")) | |
puts colorize("Error: Script must be run from the discourse-ai plugin root directory", :red) | |
exit 1 | |
end | |
end | |
def git_tracked_files | |
`git ls-files`.split("\n").map { |f| File.join(@plugin_root, f) } | |
end | |
def all_directories_with_persona | |
# Find all directories that contain 'persona' in their name | |
output = `find . -type d -name "*persona*" 2>/dev/null | grep -v "\\.git"`.split("\n") | |
output.map { |dir| File.join(@plugin_root, dir.sub("./", "")) } | |
end | |
def replace_content_in_files | |
puts colorize("Replacing content in files...", :blue) | |
file_extensions = %w[rb js gjs yml erb scss hbs json] | |
git_tracked_files.each do |file| | |
# Skip files in db/ directory and tokenizers/ | |
next if file.include?("/db/") | |
next if file.include?("/tokenizers/") | |
next if !file_extensions.include?(File.extname(file)[1..-1]) | |
next if File.basename(file) == "rename_persona_to_agent.rb" | |
next unless File.exist?(file) | |
content = File.read(file) | |
next unless content.match?(/persona/i) | |
original_content = content.dup | |
# Replace different case variations | |
content.gsub!(/persona/, "agent") | |
content.gsub!(/Persona/, "Agent") | |
content.gsub!(/PERSONA/, "AGENT") | |
# Handle special cases | |
content.gsub!(/aiPersona/, "aiAgent") | |
content.gsub!(/AIPersona/, "AIAgent") | |
content.gsub!(/ai_persona/, "ai_agent") | |
content.gsub!(/Ai_persona/, "Ai_agent") | |
content.gsub!(/ai-persona/, "ai-agent") | |
if content != original_content | |
File.write(file, content) | |
relative_path = file.sub("#{@plugin_root}/", "") | |
puts colorize("Content updated: #{relative_path}", :green) | |
@manifest << "Content updated: #{relative_path}" | |
end | |
end | |
end | |
def rename_files_and_directories | |
puts colorize("Renaming files and directories using git mv...", :blue) | |
# Get all directories with 'persona' in their path (excluding db/ and tokenizers/) | |
# Sort by depth (deepest first) to avoid conflicts when renaming parent directories | |
dirs_to_rename = | |
all_directories_with_persona | |
.select { |path| !path.include?("/db/") && !path.include?("/tokenizers/") } | |
.sort_by { |path| -path.count("/") } | |
# Get all files with 'persona' in their names (excluding db/ and tokenizers/) | |
files_to_rename = | |
git_tracked_files.select do |path| | |
!path.include?("/db/") && !path.include?("/tokenizers/") && | |
File.basename(path).match?(/persona/i) | |
end | |
# First, rename individual files that have 'persona' in their filename | |
puts colorize(" Renaming individual files with 'persona' in filename...", :blue) | |
files_to_rename.each do |old_path| | |
next unless File.exist?(old_path) | |
next if File.basename(old_path) == "rename_persona_to_agent.rb" | |
# Skip files that are inside directories we're going to rename | |
# (they'll be handled when we rename the directory) | |
next if dirs_to_rename.any? { |dir| old_path.start_with?(dir + "/") } | |
new_path = old_path.gsub(/persona/, "agent").gsub(/Persona/, "Agent") | |
if old_path != new_path | |
# Ensure parent directory exists | |
FileUtils.mkdir_p(File.dirname(new_path)) | |
# Use git mv to preserve history | |
if system("git", "mv", old_path, new_path) | |
old_relative = old_path.sub("#{@plugin_root}/", "") | |
new_relative = new_path.sub("#{@plugin_root}/", "") | |
puts colorize(" File renamed: #{old_relative} -> #{new_relative}", :green) | |
@manifest << "File renamed: #{old_relative} -> #{new_relative}" | |
else | |
puts colorize(" Failed to rename: #{old_path}", :red) | |
end | |
end | |
end | |
# Then rename directories (deepest first to avoid path conflicts) | |
puts colorize(" Renaming directories with 'persona' in path...", :blue) | |
dirs_to_rename.each do |old_dir_path| | |
next unless File.exist?(old_dir_path) && File.directory?(old_dir_path) | |
new_dir_path = old_dir_path.gsub(/persona/, "agent").gsub(/Persona/, "Agent") | |
if old_dir_path != new_dir_path && !File.exist?(new_dir_path) | |
# Create parent directory if needed | |
FileUtils.mkdir_p(File.dirname(new_dir_path)) | |
# Use git mv to preserve history for the entire directory tree | |
if system("git", "mv", old_dir_path, new_dir_path) | |
old_relative = old_dir_path.sub("#{@plugin_root}/", "") | |
new_relative = new_dir_path.sub("#{@plugin_root}/", "") | |
puts colorize(" Directory renamed: #{old_relative} -> #{new_relative}", :green) | |
@manifest << "Directory renamed: #{old_relative} -> #{new_relative}" | |
# Log all files that were moved as part of this directory rename | |
if File.directory?(new_dir_path) | |
Dir | |
.glob("#{new_dir_path}/**/*", File::FNM_DOTMATCH) | |
.each do |moved_file| | |
next if File.directory?(moved_file) | |
next if File.basename(moved_file).start_with?(".") | |
# Calculate what the old path would have been | |
relative_to_new_dir = moved_file.sub(new_dir_path + "/", "") | |
old_file_path = File.join(old_dir_path, relative_to_new_dir) | |
old_file_relative = old_file_path.sub("#{@plugin_root}/", "") | |
new_file_relative = moved_file.sub("#{@plugin_root}/", "") | |
puts colorize( | |
" File moved: #{old_file_relative} -> #{new_file_relative}", | |
:green, | |
) | |
@manifest << "File moved: #{old_file_relative} -> #{new_file_relative}" | |
end | |
end | |
else | |
puts colorize(" Failed to rename directory: #{old_dir_path}", :red) | |
end | |
end | |
end | |
end | |
def create_migration | |
puts colorize("Creating database migration to copy persona tables...", :blue) | |
timestamp = Time.now.strftime("%Y%m%d%H%M%S") | |
migration_file = | |
File.join(@plugin_root, "db", "migrate", "#{timestamp}_copy_persona_tables_to_agent.rb") | |
migration_content = <<~RUBY | |
# frozen_string_literal: true | |
class CopyPersonaTablesToAgent < ActiveRecord::Migration[7.0] | |
def up | |
# Copy the main table structure and data | |
if table_exists?(:ai_personas) && !table_exists?(:ai_agents) | |
execute <<~SQL | |
CREATE TABLE ai_agents AS | |
SELECT * FROM ai_personas | |
SQL | |
# Copy indexes from ai_personas to ai_agents | |
execute <<~SQL | |
CREATE UNIQUE INDEX index_ai_agents_on_id | |
ON ai_agents USING btree (id) | |
SQL | |
# Copy any other indexes that exist on ai_personas | |
indexes = execute(<<~SQL).to_a | |
SELECT indexname, indexdef | |
FROM pg_indexes | |
WHERE tablename = 'ai_personas' | |
AND indexname != 'ai_personas_pkey' | |
SQL | |
indexes.each do |index| | |
new_index_def = index['indexdef'].gsub('ai_personas', 'ai_agents') | |
new_index_name = index['indexname'].gsub('ai_personas', 'ai_agents') | |
new_index_def = new_index_def.gsub(index['indexname'], new_index_name) | |
execute(new_index_def) | |
end | |
end | |
# Update polymorphic associations to point to new table | |
execute <<~SQL | |
UPDATE rag_document_fragments | |
SET target_type = 'AiAgent' | |
WHERE target_type = 'AiPersona' | |
SQL | |
execute <<~SQL | |
UPDATE upload_references | |
SET target_type = 'AiAgent' | |
WHERE target_type = 'AiPersona' | |
SQL | |
end | |
def down | |
drop_table :ai_agents if table_exists?(:ai_agents) | |
# Revert polymorphic associations | |
execute <<~SQL | |
UPDATE rag_document_fragments | |
SET target_type = 'AiPersona' | |
WHERE target_type = 'AiAgent' | |
SQL | |
execute <<~SQL | |
UPDATE upload_references | |
SET target_type = 'AiPersona' | |
WHERE target_type = 'AiAgent' | |
SQL | |
end | |
end | |
RUBY | |
FileUtils.mkdir_p(File.dirname(migration_file)) | |
File.write(migration_file, migration_content) | |
relative_migration = migration_file.sub("#{@plugin_root}/", "") | |
puts colorize("Created migration file: #{relative_migration}", :green) | |
@manifest << "Created migration file: #{relative_migration}" | |
end | |
def create_post_migration | |
puts colorize("Creating post-migration to drop old persona tables...", :blue) | |
timestamp = (Time.now + 1).strftime("%Y%m%d%H%M%S") # Ensure this runs after the main migration | |
post_migrate_dir = File.join(@plugin_root, "db", "post_migrate") | |
migration_file = File.join(post_migrate_dir, "#{timestamp}_drop_persona_tables.rb") | |
migration_content = <<~RUBY | |
# frozen_string_literal: true | |
class DropPersonaTables < ActiveRecord::Migration[7.0] | |
def up | |
# Drop the old table after copying to new one | |
drop_table :ai_personas if table_exists?(:ai_personas) | |
end | |
def down | |
raise IrreversibleMigration, "Cannot recreate dropped persona tables" | |
end | |
end | |
RUBY | |
FileUtils.mkdir_p(post_migrate_dir) | |
File.write(migration_file, migration_content) | |
relative_migration = migration_file.sub("#{@plugin_root}/", "") | |
puts colorize("Created post-migration file: #{relative_migration}", :green) | |
@manifest << "Created post-migration file: #{relative_migration}" | |
end | |
def print_content_summary | |
puts colorize("Content replacement completed!", :green) | |
puts colorize("Files updated: #{@manifest.count}", :yellow) | |
@manifest.each { |change| puts " #{change}" } | |
end | |
def print_migration_summary | |
puts colorize("Database migrations created!", :green) | |
puts colorize("Files created: #{@manifest.count}", :yellow) | |
@manifest.each { |change| puts " #{change}" } | |
end | |
def print_summary | |
puts colorize("=" * 55, :blue) | |
puts colorize("Completed renaming 'persona' to 'agent' in the codebase", :green) | |
puts colorize("=" * 55, :blue) | |
puts colorize("Changes made:", :yellow) | |
@manifest.each { |change| puts " #{change}" } | |
puts colorize("Next steps:", :yellow) | |
puts "1. Review changes with 'git diff'" | |
puts "2. Run tests and fix any remaining issues" | |
puts "3. Run the database migrations (migrate, then post_migrate)" | |
puts colorize("=" * 55, :blue) | |
end | |
end | |
# Run the renamer if this file is executed directly | |
PersonaToAgentRenamer.new.run if __FILE__ == $0 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment