lib/pronto/rails_migrations.rb



module Pronto
  class RailsMigrations < Runner
    def run
      return [] unless migration_patches?

      messages = []

      if other_patches?
        patch = migration_patches.first
        messages << message(
          patch,
          'Run migrations in a separate PR from application code changes.',
          level: :error
        )
      end

      messages + bad_structure_sql_messages
    end

    private

    def migration_patches?
      migration_patches.any?
    end

    def migration_patches
      @migration_patches ||= @patches.select { |patch| migration_patch?(patch) }
    end

    def other_patches?
      @patches.reject { |patch| migration_patch?(patch) }.any?
    end

    def bad_structure_sql_messages
      patch = structure_sql_patches.first
      return [] unless patch

      structure_sql = File.read(patch.new_file_full_path)
      inserts = structure_sql.split("\n").grep(/\('\d+'\)/)
      unordered_inserts = (inserts.sort != inserts)

      *all_but_tail, tail = inserts
      bad_semicolons = all_but_tail.any? { |line| line.end_with?(';') } || !tail.end_with?(';')

      bad_ending = structure_sql[-4, 4] !~ /[^\n]\n\n\n/

      messages = []

      if unordered_inserts
        messages << message(
          patch,
          '`schema_migrations` insert values are not ordered by timestamp.'
        )
      end
      if bad_semicolons
        messages << message(
          patch,
          '`schema_migrations` inserts must end with comma (`,`), ' \
          'last insert must end with semicolon (`;`).'
        )
      end
      messages << message(patch, '`db/structure.sql` must end with 2 empty lines.') if bad_ending

      messages
    end

    def message(patch, text, level: :error)
      path = patch.delta.new_file[:path]
      line = patch.added_lines.first
      Message.new(path, line, level, text, nil, self.class)
    end

    def structure_sql_patches
      @patches.select { |patch| patch.new_file_full_path.to_s =~ /structure\.sql/ }
    end

    def migration_patch?(patch)
      path = patch.new_file_full_path.to_s
      /db.(schema.rb|structure.sql|migrate)/ =~ path
    end
  end
end