Skip to content

Instantly share code, notes, and snippets.

@punkrats
Created October 2, 2019 11:24
Show Gist options
  • Select an option

  • Save punkrats/0cf4be9cd2c168ff302da3cb0637ca15 to your computer and use it in GitHub Desktop.

Select an option

Save punkrats/0cf4be9cd2c168ff302da3cb0637ca15 to your computer and use it in GitHub Desktop.
Cleans up backup files while keeping hourly, daily, weekly and monthly backups.
#!/usr/bin/env ruby
# Cleans up backup files transferred to a remote directory.
# Place this file in the root of your backup account and it will clean up all
# subdirectories. You may prevent or customize action by a config file.
class Directory
# The script will keep 24 hourly backups by default. So up to 24 files will
# be kept that get stored on the server within one day.
# After that, the latest of the hourly backups will be kept as daily backup.
# 7 daily backups will be kept and on the 8th day, the backup of the 7th day
# will be kept as backup for the week.
# Same logic applies to weekly and monthly backups.
CONFIG = {
'hourly' => 24, # Number of hourly backups to keep
'daily' => 7, # Number of daily backups to keep
'weekly' => 4, # Number of weekly backups to keep
'monthly' => 6, # Number of monthly backups to keep
'forever' => 5, # Number of backups to keep forever
'pattern' => '*.tar.gz', # Pattern to find files
'cleanup' => 'yes' # To prevent action, set 'no' (will affect all subdirs)
}
# Each default can be overwritten in a file .bakrm like this:
# hourly: 4
# daily: 2
# weekly: 0
# monthly: 0
# forever: 4
CONFIG_FILE = '.bakrm'
def initialize(path)
@path = path
end
# Returns folders
def folders
@folders ||= Dir.chdir(@path) do
Dir.glob('*').
select { |f| File.directory?(f) }.
map { |f| File.join(@path, f) }
end
end
# Returns files, sorted by date
def files
@files ||= Dir.chdir(@path) do
Dir[pattern].
reject { |f| %[bakrm.rb #{CONFIG_FILE}].include?(f) }.
select { |f| File.file?(f) }.
sort_by { |f| File.mtime(f) }.
map { |f| File.join(@path, f) }.
reverse
end
end
def reload_files
@files = nil
files
end
# Returns config
def config
@config ||= begin
path = File.join(@path, CONFIG_FILE)
if File.exists?(path)
text = File.read(path)
values = text.scan(/(.+):\s*(.+)/)
Hash[values]
end
end
end
def cleanup
if cleanup?
cleanup_files
cleanup_folders
end
end
private
def cleanup_files
reject_empty_files
keep = keep_files
delete = reload_files - keep
# puts "keep: #{keep.count}; delete: #{delete.count}"
delete.each { |f| File.delete(f) }
end
def cleanup_folders
folders.each do |dir|
Directory.new(dir).cleanup
end
end
def config_value(key)
value = config && config[key]
value || CONFIG[key]
end
def cleanup?
config_value('cleanup') == 'yes'
end
def pattern
config_value('pattern')
end
def hourly
config_value('hourly').to_i
end
def daily
config_value('daily').to_i
end
def weekly
config_value('weekly').to_i
end
def monthly
config_value('monthly').to_i
end
def forever
config_value('forever')
end
def keep_files
keep = []
keep += hourly_files if hourly > 0
keep += daily_files if daily > 0
keep += weekly_files if weekly > 0
keep += monthly_files if monthly > 0
keep.uniq!
if keep.length < forever
if files.length < forever
keep = files
else
keep = files[0, forever]
end
end
keep
end
def hourly_files
pick_files(hourly, 3600)
end
def daily_files
pick_files(daily, 3600*24)
end
def weekly_files
pick_files(weekly, 3600*24*7)
end
def monthly_files
pick_files(monthly, 3600*24*30)
end
# Remove empty files from list
def reject_empty_files
files.reject! { |f| File.size(f) == 0 }
end
# Pick files matching given time interval.
def pick_files(counter, increment)
picked = []
range_start = now
counter.times do
range_end = range_start - increment
files.each do |f|
mtime = File.mtime(f)
if mtime < range_start && mtime >= range_end
picked << f
break
end
end
range_start = range_end
end
picked
end
def now
@now ||= Time.now
end
end
def run
Directory.new(Dir.pwd).cleanup
end
run
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment