Created
October 2, 2019 11:24
-
-
Save punkrats/0cf4be9cd2c168ff302da3cb0637ca15 to your computer and use it in GitHub Desktop.
Cleans up backup files while keeping hourly, daily, weekly and monthly backups.
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 | |
| # 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