Skip to content

Instantly share code, notes, and snippets.

@trueheart78
Last active March 12, 2026 15:51
Show Gist options
  • Select an option

  • Save trueheart78/223b2874bd495b6c9d73befd3a1aa416 to your computer and use it in GitHub Desktop.

Select an option

Save trueheart78/223b2874bd495b6c9d73befd3a1aa416 to your computer and use it in GitHub Desktop.
Kitty version checker πŸˆβ€β¬›
#!/usr/bin/env ruby
# frozen_string_literal: true
##
# Ruby script to update the installed version of the kitty terminal and update
# the default logo and icons to the custom versions I prefer.
#
# GIST URL: https://gist.github.com/trueheart78/223b2874bd495b6c9d73befd3a1aa416
##
CUSTOM_ICONS = true
HOME_DIR = ENV.fetch('HOME', nil)
CUSTOM_ICON_LOGO_PATH = "#{HOME_DIR}/dotfiles/files/kitty/icons/emoji_kitty.icns"
CUSTOM_ICON_128_PATH = "#{HOME_DIR}/dotfiles/files/kitty/icons/kitty-128x128.png"
CUSTOM_ICON_256_PATH = "#{HOME_DIR}/dotfiles/files/kitty/icons/kitty-256x256.png"
begin
require 'colorize'
require 'optparse'
require 'pry'
rescue LoadError => e
puts "=> Error: Unable to load required gem: #{e}"
exit(1)
end
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
def options
return @options unless @options.nil?
@options = {}
OptionParser.new do |parser|
parser.banner = 'Usage: kitty-update [options]'
parser.on('-c', '--check', 'Check if an update is available') do |c|
options[:check] = c
end
parser.on('-f', '--force', 'Force the install regardless if an update is available') do |f|
options[:force] = f
end
parser.on('-d', '--dry-run', 'Perform a dry run without installing or updating anything') do |d|
options[:dry_run] = d
end
parser.on('-i', '--icons',
'Update the app to use the custom icon files (defined in the script)') do |i|
options[:icons] = i
end
parser.on('-h', '--help', 'Prints this help') do
puts parser
exit
end
end.parse!
@options
end
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
def check_version?
options[:check] == true
end
def dry_run?
options[:dry_run] == true
end
def forcing?
options[:force] == true
end
def updating_icons?
options[:icons] == true
end
def fresh_install?
!installed?
end
def installed?
@installed ||= system('which kitty >/dev/null 2>&1')
end
# => "0.44.1"
def installed_version
return '0.0.0' unless installed?
@installed_version ||= `command kitty --version`.match(/(\d+\.\d+\.\d+)/)[1]
rescue NoMethodError
@installed_version = '0.0.0'
end
# => "0.45.0"
def available_version
@constants_py ||= `command curl --silent https://raw.githubusercontent.com/kovidgoyal/kitty/master/kitty/constants.py`
@constants_py.match(/Version\((\d+, \d+, \d+)\)$/)[1].split(',').map(&:to_i).join('.')
rescue NoMethodError
'0.0.0'
end
def version_update?
return if available_version == '0.0.0'
installed_version != available_version
end
# rubocop:disable Metrics/MethodLength, Metrics/AbcSize
def inform_user
if forcing?
puts '=> Reinstalling...'.blue
elsif fresh_install?
print '=> Fresh kitty version available!'.light_blue
puts "(v#{available_version})".light_blue
puts '=> Changelog: https://sw.kovidgoyal.net/kitty/changelog/'.light_blue
elsif version_update?
print '=> New kitty version available!'.light_blue
puts "(v#{available_version}) [installed: v#{installed_version}] πŸ†•".light_blue
puts '=> Changelog: https://sw.kovidgoyal.net/kitty/changelog/'.light_blue
else
puts "=> You're on the most recent version! (v#{installed_version}) βœ…".green
end
end
# rubocop:enable Metrics/MethodLength, Metrics/AbcSize
def user_consents?
puts
if installed?
print '=> Update? (Y/N) '.blue
else
print '=> Install? (Y/N) '.blue
end
answer = ''
answer = gets.chomp while answer.length.zero?
answer[0].upcase == 'Y'
end
def install_app
puts "==> Downloading and installing v#{available_version}...".blue
if dry_run?
puts '===> Skipping install command [dry run]'.cyan
else
system 'command curl -L https://sw.kovidgoyal.net/kitty/installer.sh | sh /dev/stdin'
end
puts '==> Update complete!'.blue
end
def custom_icons?
CUSTOM_ICONS &&
CUSTOM_ICON_LOGO_PATH.length.positive? &&
CUSTOM_ICON_128_PATH.length.positive? &&
CUSTOM_ICON_256_PATH.length.positive?
end
def check_icons_exist!
return unless custom_icons?
raise "File does not exist: #{CUSTOM_ICON_LOGO_PATH}" unless File.exist? CUSTOM_ICON_LOGO_PATH
raise "File does not exist: #{CUSTOM_ICON_128_PATH}" unless File.exist? CUSTOM_ICON_128_PATH
raise "File does not exist: #{CUSTOM_ICON_256_PATH}" unless File.exist? CUSTOM_ICON_256_PATH
end
# Updates the kitty app's Info.plist to point to a custom file (copied to its app install location)
#
# rubocop:disable Layout/LineLength
def update_icons
check_icons_exist!
## Backup the logo and icons
puts '==> Backing up the logo and icon files...'.blue
system 'mv /Applications/kitty.app/Contents/Resources/kitty.icns{,-bak.icns}'
system 'mv /Applications/kitty.app/Contents/Resources/kitty/logo/kitty-128.png{,-bak.png}'
system 'mv /Applications/kitty.app/Contents/Resources/kitty/logo/kitty.png{,-bak.png}'
puts '==> Backup complete!'.blue
## Copy the preferred logo and icons into the expected location
puts '==> Updating the logo and icons to use your custom files...'.blue
system "cp #{CUSTOM_ICON_LOGO_PATH} /Applications/kitty.app/Contents/Resources/kitty.icns"
system "cp #{CUSTOM_ICON_128_PATH} /Applications/kitty.app/Contents/Resources/kitty/logo/kitty-128.png"
system "cp #{CUSTOM_ICON_256_PATH} /Applications/kitty.app/Contents/Resources/kitty/logo/kitty.png"
puts '==> Update complete!'.blue
end
# rubocop:enable Layout/LineLength
if updating_icons?
unless custom_icons?
puts '=> Custom ICONS are not currently setup (see top of file for settings)'.red
exit 1
end
update_icons
elsif forcing?
inform_user
install_app
elsif check_version?
inform_user
install_app if version_update? && user_consents?
elsif version_update? || fresh_install?
inform_user
install_app if user_consents?
else
puts "=> Kitty is up to date! (v#{installed_version}) πŸ‘".blue
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment