daily_weekly_monthly 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.codeclimate.yml +30 -0
- data/.gitignore +3 -0
- data/.rspec +1 -0
- data/.rubocop.yml +29 -0
- data/.travis.yml +21 -0
- data/Gemfile +4 -0
- data/README.md +68 -0
- data/Rakefile +15 -0
- data/bin/daily_weekly_monthly +7 -0
- data/daily_weekly_monthly.gemspec +25 -0
- data/features/daily_weekly_monthly.feature +23 -0
- data/features/step_definitions/daily_weekly_monthly_steps.rb +45 -0
- data/features/support/env.rb +13 -0
- data/lib/daily_weekly_monthly.rb +7 -0
- data/lib/daily_weekly_monthly/cleaner.rb +23 -0
- data/lib/daily_weekly_monthly/cli.rb +78 -0
- data/lib/daily_weekly_monthly/creator.rb +18 -0
- data/lib/daily_weekly_monthly/downloader.rb +15 -0
- data/lib/daily_weekly_monthly/processor.rb +17 -0
- data/lib/daily_weekly_monthly/runner.rb +42 -0
- data/lib/daily_weekly_monthly/version.rb +4 -0
- data/spec/daily_weekly_monthly/cleaner_spec.rb +30 -0
- data/spec/daily_weekly_monthly/cli_spec.rb +44 -0
- data/spec/daily_weekly_monthly/creator_spec.rb +22 -0
- data/spec/daily_weekly_monthly/downloader_spec.rb +22 -0
- data/spec/daily_weekly_monthly/processor_spec.rb +33 -0
- data/spec/daily_weekly_monthly/runner_spec.rb +48 -0
- data/spec/daily_weekly_monthly_spec.rb +19 -0
- data/spec/spec_helper.rb +2 -0
- metadata +144 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 13f9aa478c619dd93fce91053cf47c992ebadf08
|
4
|
+
data.tar.gz: abf25ce04f627f2f46db301c95105f0da4c0efc1
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3d617e0f31b33e196e1ad416319634e524b4d56299beb878b3344a49b9eb6d7de7054918d6f9b45e0e9f2a999cc3be72940f6869b2e1a739b509792ccb99a400
|
7
|
+
data.tar.gz: 001f24d43f022ec1a780e972165036e1121766a93ad37520d8fe8034aff2eb26a6d5c81157a124205a823088d535f88cb355ba8f9164b6aaff67452c0b20e04c
|
data/.codeclimate.yml
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
---
|
2
|
+
engines:
|
3
|
+
duplication:
|
4
|
+
enabled: true
|
5
|
+
config:
|
6
|
+
languages:
|
7
|
+
- ruby
|
8
|
+
- javascript
|
9
|
+
- python
|
10
|
+
- php
|
11
|
+
fixme:
|
12
|
+
enabled: true
|
13
|
+
rubocop:
|
14
|
+
enabled: true
|
15
|
+
bundler-audit:
|
16
|
+
enabled: true
|
17
|
+
reek:
|
18
|
+
enabled: true
|
19
|
+
ratings:
|
20
|
+
paths:
|
21
|
+
- "**.inc"
|
22
|
+
- "**.js"
|
23
|
+
- "**.jsx"
|
24
|
+
- "**.module"
|
25
|
+
- "**.php"
|
26
|
+
- "**.py"
|
27
|
+
- "**.rb"
|
28
|
+
exclude_paths:
|
29
|
+
- features/
|
30
|
+
- spec/
|
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
Style/StringLiterals:
|
2
|
+
EnforcedStyle: double_quotes
|
3
|
+
Style/TrailingCommaInLiteral:
|
4
|
+
EnforcedStyleForMultiline: comma
|
5
|
+
Style/TrailingCommaInArguments:
|
6
|
+
EnforcedStyleForMultiline: comma
|
7
|
+
Style/NestedParenthesizedCalls:
|
8
|
+
Enabled: false
|
9
|
+
Documentation:
|
10
|
+
Enabled: false
|
11
|
+
Metrics/LineLength:
|
12
|
+
Max: 120
|
13
|
+
Performance/RedundantMerge:
|
14
|
+
Enabled: false
|
15
|
+
Style/SpaceInsideBlockBraces:
|
16
|
+
SpaceBeforeBlockParameters: false
|
17
|
+
Style/MethodDefParentheses:
|
18
|
+
EnforcedStyle: require_no_parentheses
|
19
|
+
Style/PredicateName:
|
20
|
+
NamePrefixBlacklist:
|
21
|
+
- is_
|
22
|
+
Style/AndOr:
|
23
|
+
EnforcedStyle: conditionals
|
24
|
+
Style/SpaceInsideHashLiteralBraces:
|
25
|
+
EnforcedStyle: no_space
|
26
|
+
Style/BlockDelimiters:
|
27
|
+
EnforcedStyle: semantic
|
28
|
+
Metrics/MethodLength:
|
29
|
+
Max: 5
|
data/.travis.yml
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
language: ruby
|
2
|
+
branches:
|
3
|
+
only:
|
4
|
+
- master
|
5
|
+
- /^v[0-9]+\.[0-9]+\.[0-9]+/
|
6
|
+
rvm:
|
7
|
+
- ruby-head
|
8
|
+
- jruby-1.7.19
|
9
|
+
- 2.0.0
|
10
|
+
- 2.3.0
|
11
|
+
script:
|
12
|
+
- bundle exec rake
|
13
|
+
deploy:
|
14
|
+
provider: rubygems
|
15
|
+
api_key:
|
16
|
+
secure: clYHhpfYieW4ZMPlDwhdsxZAaca6Ltg8fSDtmQcNly40+fOhAhBnR+VFX6VODX3jx0Jv0MlIZRqWn4V9LVERaEMvKwSL2qNnk2sdMe9Kbh+lXD4auCjgHhFKXN6dzUfXmJYKzjuUaMtK4m7aBLKnm2JLm3omUa4c0Yrnj3qS/to=
|
17
|
+
on:
|
18
|
+
tags: true
|
19
|
+
all_branches: true
|
20
|
+
repo: tpbowden/db_backup
|
21
|
+
rvm: 2.0.0
|
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
# Daily Weekly Monthly
|
2
|
+
|
3
|
+
Keep your data safe by backing up on a daily, weekly and monthly basis.
|
4
|
+
|
5
|
+
This tool allows you to take regular backups whilst automatically removing old ones.
|
6
|
+
Backups are stored in <backup location>/(daily|weekly|monthly), and files are named using
|
7
|
+
the format YYYY-MM-DD.<extension>
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
Using bundler:
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
gem "daily_weekly_monthly"
|
15
|
+
```
|
16
|
+
|
17
|
+
CLI:
|
18
|
+
|
19
|
+
```sh
|
20
|
+
gem install daily_weekly_monthly
|
21
|
+
```
|
22
|
+
|
23
|
+
## Use
|
24
|
+
|
25
|
+
To use in your code, there is a single wrapper function to call:
|
26
|
+
|
27
|
+
```ruby
|
28
|
+
DailyWeeklyMonthly.start(backup_command, options)
|
29
|
+
```
|
30
|
+
|
31
|
+
Where backup command is a string of the shell command you wish to run to get the database backup (to STDOUT),
|
32
|
+
and options are a hash of options which are explained in detail below.
|
33
|
+
|
34
|
+
|
35
|
+
### CLI
|
36
|
+
|
37
|
+
To use from the command line, just run the following after installing the gem:
|
38
|
+
|
39
|
+
daily_weekly_monthly "your backup command" [options]
|
40
|
+
|
41
|
+
Again, the options are explained below.
|
42
|
+
|
43
|
+
|
44
|
+
## Configuration
|
45
|
+
|
46
|
+
Config is passed to the gem as a hash, with all possible options, their defaults and command line flags:
|
47
|
+
|
48
|
+
```ruby
|
49
|
+
{
|
50
|
+
backups_dir: "~/backups", # Where on the file system to store the backups
|
51
|
+
output_extension: "pgdump.gz", # File extension to be appended to each backup
|
52
|
+
day_of_week: 1, # Which day of the week to run weekly backups (0 - 6 where 0 is Sunday and 6 is Saturday)
|
53
|
+
day_of_month: 1, # Which day in the month to run monthly backups
|
54
|
+
days_to_keep: 7, # Number of daily backups to store
|
55
|
+
weeks_to_keep: 5, # Number of weekly backups to store
|
56
|
+
months_to_keep: 12, # Number of monthly backups to store
|
57
|
+
}
|
58
|
+
```
|
59
|
+
|
60
|
+
### Command line arguments
|
61
|
+
|
62
|
+
-d, --dir=d Backups directory path
|
63
|
+
-e, --ext=e Backup file extension
|
64
|
+
-w, --week-day=n Week day to run on
|
65
|
+
-m, --month-day=n Day of month to run on
|
66
|
+
-D, --days-to-keep=n Daily backups to keep
|
67
|
+
-W, --weeks-to-keep=n Weekly backups to keep
|
68
|
+
-M, --months-to-keep=n Monthly backups to keep
|
data/Rakefile
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require "rubocop/rake_task"
|
2
|
+
require "rspec/core/rake_task"
|
3
|
+
require "cucumber/rake/task"
|
4
|
+
|
5
|
+
RSpec::Core::RakeTask.new "rspec"
|
6
|
+
RuboCop::RakeTask.new
|
7
|
+
Cucumber::Rake::Task.new "cucumber" do |t|
|
8
|
+
t.cucumber_opts = "features --format progress"
|
9
|
+
end
|
10
|
+
|
11
|
+
task default: [
|
12
|
+
:rubocop,
|
13
|
+
:rspec,
|
14
|
+
:cucumber,
|
15
|
+
]
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
$LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
|
3
|
+
require "daily_weekly_monthly/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "daily_weekly_monthly"
|
7
|
+
s.version = DailyWeeklyMonthly::VERSION
|
8
|
+
s.date = "2016-02-28"
|
9
|
+
s.summary = "Manage database backups"
|
10
|
+
s.description = "Automatically manage daily, weekly and monthly database backups"
|
11
|
+
s.authors = ["Tom Bowden"]
|
12
|
+
s.files = `git ls-files`.split($RS)
|
13
|
+
s.bindir = "bin"
|
14
|
+
s.executables = "daily_weekly_monthly"
|
15
|
+
s.email = "tom.b1992@gmail.com"
|
16
|
+
s.homepage = "http://github.com/tpbowden/daily_weekly_monthly"
|
17
|
+
s.license = "MIT"
|
18
|
+
s.required_ruby_version = ">= 2.0.0"
|
19
|
+
|
20
|
+
s.add_development_dependency "rake", ">= 10.4"
|
21
|
+
s.add_development_dependency "rubocop", ">= 0.35"
|
22
|
+
s.add_development_dependency "rspec", ">= 3.4"
|
23
|
+
s.add_development_dependency "simplecov", ">= 0.11"
|
24
|
+
s.add_development_dependency "cucumber", ">= 2.0"
|
25
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
Feature: Daily, weekly and monthly backups
|
2
|
+
|
3
|
+
Scenario Outline: Creating new backups
|
4
|
+
Given it is the correct day for <Period> backups
|
5
|
+
When I run the backups
|
6
|
+
Then I can see a <Period> backup file
|
7
|
+
And the <Period> backup file contains the backup command contents
|
8
|
+
Examples:
|
9
|
+
| Period |
|
10
|
+
| daily |
|
11
|
+
| weekly |
|
12
|
+
| monthly |
|
13
|
+
|
14
|
+
Scenario Outline: Cleaning up old backups
|
15
|
+
Given it is the correct day for <Period> backups
|
16
|
+
And there are old <Period> backups
|
17
|
+
When I run the backups
|
18
|
+
Then the old <Period> backups have been removed
|
19
|
+
Examples:
|
20
|
+
| Period |
|
21
|
+
| daily |
|
22
|
+
| weekly |
|
23
|
+
| monthly |
|
@@ -0,0 +1,45 @@
|
|
1
|
+
Given "it is the correct day for daily backups" do
|
2
|
+
# noop
|
3
|
+
end
|
4
|
+
|
5
|
+
Given "it is the correct day for weekly backups" do
|
6
|
+
@weekly_backup_day = Date.today.wday
|
7
|
+
end
|
8
|
+
|
9
|
+
Given "it is the correct day for monthly backups" do
|
10
|
+
@monthly_backup_day = Date.today.day
|
11
|
+
end
|
12
|
+
|
13
|
+
Given(/there are old (.+) backups/) do |period|
|
14
|
+
FileUtils.mkdir_p File.join(backups_dir, period)
|
15
|
+
|
16
|
+
%w(2001 2002 2003 2004 2005 2006 2007 2008 2009 2010).each do |year|
|
17
|
+
File.write(File.join(backups_dir, period, "#{year}-01-01.pgdump.gz"), "foo\n")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
When "I run the backups" do
|
22
|
+
DailyWeeklyMonthly.start("echo 'foo';", backups_dir: backups_dir,
|
23
|
+
day_of_week: @weekly_backup_day || 1,
|
24
|
+
day_of_month: @monthly_backup_day || 1,
|
25
|
+
days_to_keep: 3,
|
26
|
+
weeks_to_keep: 3,
|
27
|
+
months_to_keep: 3)
|
28
|
+
end
|
29
|
+
|
30
|
+
Then(/I can see a (.+) backup file/) do |period|
|
31
|
+
expect(File.exist?(File.join(backups_dir, period, "#{Date.today.iso8601}.pgdump.gz"))).to be true
|
32
|
+
end
|
33
|
+
|
34
|
+
Then(/the (.+) backup file contains the backup command contents/) do |period|
|
35
|
+
expect(File.read(File.join(backups_dir, period, "#{Date.today.iso8601}.pgdump.gz"))).to eq "foo\n"
|
36
|
+
end
|
37
|
+
|
38
|
+
Then(/the old (.+) backups have been removed/) do |period|
|
39
|
+
files = Dir[File.join(backups_dir, period, "*.pgdump.gz")].map {|f| File.basename f }
|
40
|
+
expect(files).to match_array [
|
41
|
+
"2009-01-01.pgdump.gz",
|
42
|
+
"2010-01-01.pgdump.gz",
|
43
|
+
"#{Date.today.iso8601}.pgdump.gz",
|
44
|
+
]
|
45
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require "fileutils"
|
2
|
+
|
3
|
+
module DailyWeeklyMonthly
|
4
|
+
class Cleaner
|
5
|
+
def initialize backups_dir, output_extension
|
6
|
+
@backups_dir = backups_dir
|
7
|
+
@output_extension = output_extension
|
8
|
+
end
|
9
|
+
|
10
|
+
def call dir, number_to_keep
|
11
|
+
old_backups(dir, number_to_keep).each do |file|
|
12
|
+
FileUtils.rm file
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def old_backups dir, number_to_keep
|
17
|
+
Dir[File.join(@backups_dir, dir, "*.#{@output_extension}")]
|
18
|
+
.sort
|
19
|
+
.reverse
|
20
|
+
.drop(number_to_keep)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require "optparse"
|
2
|
+
require "daily_weekly_monthly"
|
3
|
+
|
4
|
+
module DailyWeeklyMonthly
|
5
|
+
class Cli
|
6
|
+
def initialize args
|
7
|
+
@options = {}
|
8
|
+
@command = parse args
|
9
|
+
raise "Please supply a command to run" if @command.empty?
|
10
|
+
@command = @command.join(" ")
|
11
|
+
end
|
12
|
+
|
13
|
+
def call
|
14
|
+
DailyWeeklyMonthly.start @command, @options
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
# rubocop : disable Metrics/MethodLength
|
20
|
+
def parse args
|
21
|
+
args.options do |opts|
|
22
|
+
opts.banner = "Usage: daily_weekly_monthly \"database backup command\" [options]"
|
23
|
+
parse_dir(opts)
|
24
|
+
parse_ext(opts)
|
25
|
+
parse_week_day(opts)
|
26
|
+
parse_month_day(opts)
|
27
|
+
parse_keep_days(opts)
|
28
|
+
parse_keep_weeks(opts)
|
29
|
+
parse_keep_months(opts)
|
30
|
+
opts.parse!
|
31
|
+
end
|
32
|
+
|
33
|
+
args
|
34
|
+
end
|
35
|
+
|
36
|
+
def parse_keep_months opts
|
37
|
+
opts.on("-M n", "--months-to-keep=n", "Monthly backups to keep", OptionParser::DecimalInteger) do |n|
|
38
|
+
@options[:months_to_keep] = n
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def parse_keep_weeks opts
|
43
|
+
opts.on("-W n", "--weeks-to-keep=n", "Weekly backups to keep", OptionParser::DecimalInteger) do |n|
|
44
|
+
@options[:weeks_to_keep] = n
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def parse_keep_days opts
|
49
|
+
opts.on("-D n", "--days-to-keep=n", "Daily backups to keep", OptionParser::DecimalInteger) do |n|
|
50
|
+
@options[:days_to_keep] = n
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def parse_month_day opts
|
55
|
+
opts.on("-m n", "--month-day=n", "Day of month to run on", OptionParser::DecimalInteger) do |n|
|
56
|
+
@options[:day_of_month] = n
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def parse_ext opts
|
61
|
+
opts.on("-e e", "--ext=e", "Backup file extension", String) do |e|
|
62
|
+
@options[:output_extension] = e
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def parse_week_day opts
|
67
|
+
opts.on("-w n", "--week-day=n", "Week day to run on", OptionParser::DecimalInteger) do |n|
|
68
|
+
@options[:day_of_week] = n
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def parse_dir opts
|
73
|
+
opts.on("-d d", "--dir=d", "Backups directory path", String) do |d|
|
74
|
+
@options[:backups_dir] = d
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require "fileutils"
|
2
|
+
require "date"
|
3
|
+
|
4
|
+
module DailyWeeklyMonthly
|
5
|
+
class Creator
|
6
|
+
def initialize backup, backups_dir, output_extension
|
7
|
+
@backup = backup
|
8
|
+
@backups_dir = backups_dir
|
9
|
+
@output_extension = output_extension
|
10
|
+
end
|
11
|
+
|
12
|
+
def call dir
|
13
|
+
FileUtils.mkdir_p File.join(@backups_dir, dir)
|
14
|
+
output_path = File.join(@backups_dir, dir, "#{Date.today.iso8601}.#{@output_extension}")
|
15
|
+
File.write output_path, @backup
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require "English"
|
2
|
+
|
3
|
+
module DailyWeeklyMonthly
|
4
|
+
class Downloader
|
5
|
+
def initialize backup_command
|
6
|
+
@backup_command = backup_command
|
7
|
+
end
|
8
|
+
|
9
|
+
def call
|
10
|
+
result = `#{@backup_command}`
|
11
|
+
raise "Failed to download backup" unless $CHILD_STATUS == 0
|
12
|
+
result
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require "daily_weekly_monthly/creator"
|
2
|
+
require "daily_weekly_monthly/cleaner"
|
3
|
+
|
4
|
+
module DailyWeeklyMonthly
|
5
|
+
class Processor
|
6
|
+
def initialize backup, backups_dir, output_extension
|
7
|
+
@backup = backup
|
8
|
+
@creator = Creator.new backup, backups_dir, output_extension
|
9
|
+
@cleaner = Cleaner.new backups_dir, output_extension
|
10
|
+
end
|
11
|
+
|
12
|
+
def call period, options
|
13
|
+
@creator.call period
|
14
|
+
@cleaner.call period, options.fetch(:keep)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require "daily_weekly_monthly/processor"
|
2
|
+
require "daily_weekly_monthly/downloader"
|
3
|
+
|
4
|
+
module DailyWeeklyMonthly
|
5
|
+
class Runner
|
6
|
+
DEFAULTS = {
|
7
|
+
backups_dir: File.expand_path("~/backups"),
|
8
|
+
output_extension: "pgdump.gz",
|
9
|
+
day_of_week: 1,
|
10
|
+
day_of_month: 1,
|
11
|
+
days_to_keep: 7,
|
12
|
+
weeks_to_keep: 5,
|
13
|
+
months_to_keep: 12,
|
14
|
+
}.freeze
|
15
|
+
|
16
|
+
def initialize backup_command, options = {}
|
17
|
+
@backup_command = backup_command
|
18
|
+
@options = DEFAULTS.merge(options)
|
19
|
+
end
|
20
|
+
|
21
|
+
def call
|
22
|
+
processor = Processor.new(backup, @options[:backups_dir], @options[:output_extension])
|
23
|
+
processor.call("daily", keep: @options[:days_to_keep])
|
24
|
+
processor.call("weekly", keep: @options[:weeks_to_keep]) if weekly_backup?
|
25
|
+
processor.call("monthly", keep: @options[:months_to_keep]) if monthly_backup?
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def weekly_backup?
|
31
|
+
Date.today.wday == @options[:day_of_week]
|
32
|
+
end
|
33
|
+
|
34
|
+
def monthly_backup?
|
35
|
+
Date.today.day == @options[:day_of_month]
|
36
|
+
end
|
37
|
+
|
38
|
+
def backup
|
39
|
+
Downloader.new(@backup_command).call
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "tmpdir"
|
3
|
+
require "daily_weekly_monthly/cleaner"
|
4
|
+
|
5
|
+
describe DailyWeeklyMonthly::Cleaner do
|
6
|
+
let(:backups_dir) { Dir.mktmpdir }
|
7
|
+
let(:ext) { "txt" }
|
8
|
+
|
9
|
+
subject { described_class.new backups_dir, ext }
|
10
|
+
|
11
|
+
describe "#call" do
|
12
|
+
before do
|
13
|
+
FileUtils.mkdir_p(File.join backups_dir, "weekly")
|
14
|
+
%w(2015 2016 2017 2016 2017 2018 2019 2020).each do |year|
|
15
|
+
File.write File.join(backups_dir, "weekly", "#{year}-01-01.txt"), "some content"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
it "removes all files except the newest N from the target directory" do
|
20
|
+
subject.call "weekly", 2
|
21
|
+
expect(Dir[File.join(backups_dir, "weekly", "*.#{ext}")].map { |f|
|
22
|
+
File.basename(f)
|
23
|
+
}).to match_array ["2020-01-01.txt", "2019-01-01.txt"]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
after do
|
28
|
+
FileUtils.remove_entry backups_dir
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "daily_weekly_monthly/cli"
|
3
|
+
|
4
|
+
describe DailyWeeklyMonthly::Cli do
|
5
|
+
describe "#call" do
|
6
|
+
subject { described_class.new ARGV }
|
7
|
+
|
8
|
+
context "when no command is given" do
|
9
|
+
before do
|
10
|
+
ARGV.replace []
|
11
|
+
end
|
12
|
+
|
13
|
+
it "raises an error" do
|
14
|
+
expect { subject.call }.to raise_error RuntimeError, "Please supply a command to run"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
context "with arguments and a command" do
|
19
|
+
before do
|
20
|
+
ARGV.replace [
|
21
|
+
"-M", "5",
|
22
|
+
"-W", "3",
|
23
|
+
"-D", "2",
|
24
|
+
"-m", "1",
|
25
|
+
"-e", "foo",
|
26
|
+
"-w", "2",
|
27
|
+
"-d", "bar",
|
28
|
+
"some", "command"
|
29
|
+
]
|
30
|
+
end
|
31
|
+
|
32
|
+
it "passes the parsed arguments and command to DbBackup.start" do
|
33
|
+
expect(DailyWeeklyMonthly).to receive(:start).with("some command", months_to_keep: 5,
|
34
|
+
weeks_to_keep: 3,
|
35
|
+
days_to_keep: 2,
|
36
|
+
day_of_month: 1,
|
37
|
+
output_extension: "foo",
|
38
|
+
day_of_week: 2,
|
39
|
+
backups_dir: "bar")
|
40
|
+
subject.call
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "tmpdir"
|
3
|
+
require "daily_weekly_monthly/creator"
|
4
|
+
|
5
|
+
describe DailyWeeklyMonthly::Creator do
|
6
|
+
let(:backup) { "some content" }
|
7
|
+
let(:backups_dir) { Dir.mktmpdir }
|
8
|
+
let(:ext) { "txt" }
|
9
|
+
|
10
|
+
subject { described_class.new backup, backups_dir, ext }
|
11
|
+
|
12
|
+
describe "#call" do
|
13
|
+
it "writes a file using today's date into the correct backup directory" do
|
14
|
+
subject.call "weekly"
|
15
|
+
expect(File.read(File.join(backups_dir, "weekly", "#{Date.today.iso8601}.#{ext}"))).to eq backup
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
after do
|
20
|
+
FileUtils.remove_entry backups_dir
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "daily_weekly_monthly/downloader"
|
3
|
+
|
4
|
+
describe DailyWeeklyMonthly::Downloader do
|
5
|
+
describe "#call" do
|
6
|
+
context "when the command fails" do
|
7
|
+
subject { described_class.new "commandThatAlwaysFails 2> /dev/null" }
|
8
|
+
|
9
|
+
it "raises a runtime error" do
|
10
|
+
expect { subject.call }.to raise_error RuntimeError, "Failed to download backup"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
context "when the command succeeds" do
|
15
|
+
subject { described_class.new "echo 'foo'" }
|
16
|
+
|
17
|
+
it "returns the output of the command" do
|
18
|
+
expect(subject.call).to eq "foo\n"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "daily_weekly_monthly/processor"
|
3
|
+
|
4
|
+
describe DailyWeeklyMonthly::Processor do
|
5
|
+
let(:backup) { double :backup }
|
6
|
+
let(:dir) { double :dir }
|
7
|
+
let(:ext) { double :ext }
|
8
|
+
let(:creator) { double :creator, call: true }
|
9
|
+
let(:cleaner) { double :cleaner, call: true }
|
10
|
+
|
11
|
+
subject { described_class.new backup, dir, ext }
|
12
|
+
|
13
|
+
before do
|
14
|
+
allow(DailyWeeklyMonthly::Creator).to receive(:new).with(backup, dir, ext) { creator }
|
15
|
+
allow(DailyWeeklyMonthly::Cleaner).to receive(:new).with(dir, ext) { cleaner }
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "#call" do
|
19
|
+
let(:period) { "weekly" }
|
20
|
+
let(:options) { {keep: to_keep} }
|
21
|
+
let(:to_keep) { 5 }
|
22
|
+
|
23
|
+
it "calls the creator with the period" do
|
24
|
+
expect(creator).to receive(:call).with(period)
|
25
|
+
subject.call period, options
|
26
|
+
end
|
27
|
+
|
28
|
+
it "calls the cleaner with the period and number to keep" do
|
29
|
+
expect(cleaner).to receive(:call).with(period, to_keep)
|
30
|
+
subject.call period, options
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "daily_weekly_monthly/runner"
|
3
|
+
|
4
|
+
describe DailyWeeklyMonthly::Runner do
|
5
|
+
let(:command) { double :command }
|
6
|
+
let(:downloader) { double :downloader }
|
7
|
+
let(:backup) { double :backup }
|
8
|
+
let(:backups_dir) { double :backups_dir }
|
9
|
+
let(:ext) { double :ext }
|
10
|
+
let(:processor) { double :processor, call: true }
|
11
|
+
let(:options) {
|
12
|
+
{
|
13
|
+
backups_dir: backups_dir,
|
14
|
+
output_extension: ext,
|
15
|
+
}
|
16
|
+
}
|
17
|
+
|
18
|
+
subject { described_class.new command, options }
|
19
|
+
|
20
|
+
before do
|
21
|
+
allow(DailyWeeklyMonthly::Downloader).to receive(:new).with(command) { downloader }
|
22
|
+
allow(downloader).to receive(:call) { backup }
|
23
|
+
allow(DailyWeeklyMonthly::Processor).to receive(:new).with(backup, backups_dir, ext) { processor }
|
24
|
+
end
|
25
|
+
|
26
|
+
context "when the day of the month is backup day" do
|
27
|
+
subject { described_class.new command, options.merge(day_of_month: Date.today.day) }
|
28
|
+
|
29
|
+
it "runs the monthly backup" do
|
30
|
+
expect(processor).to receive(:call).with("monthly", keep: 12)
|
31
|
+
subject.call
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context "when the weekday is the backup day" do
|
36
|
+
subject { described_class.new command, options.merge(day_of_week: Date.today.wday) }
|
37
|
+
|
38
|
+
it "runs the weekly backup" do
|
39
|
+
expect(processor).to receive(:call).with("weekly", keep: 5)
|
40
|
+
subject.call
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
it "runs the daily backup" do
|
45
|
+
expect(processor).to receive(:call).with("daily", keep: 7)
|
46
|
+
subject.call
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "daily_weekly_monthly"
|
3
|
+
|
4
|
+
describe DailyWeeklyMonthly do
|
5
|
+
describe ".start" do
|
6
|
+
let(:runner) { double :runner }
|
7
|
+
let(:command) { double :command }
|
8
|
+
let(:options) { double :options }
|
9
|
+
|
10
|
+
before do
|
11
|
+
allow(DailyWeeklyMonthly::Runner).to receive(:new).with(command, options) { runner }
|
12
|
+
end
|
13
|
+
|
14
|
+
it "passes the arguments to the runner" do
|
15
|
+
expect(runner).to receive(:call)
|
16
|
+
described_class.start command, options
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,144 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: daily_weekly_monthly
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Tom Bowden
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-02-28 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rake
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '10.4'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '10.4'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rubocop
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.35'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.35'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.4'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.4'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: simplecov
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.11'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0.11'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: cucumber
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '2.0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '2.0'
|
83
|
+
description: Automatically manage daily, weekly and monthly database backups
|
84
|
+
email: tom.b1992@gmail.com
|
85
|
+
executables:
|
86
|
+
- daily_weekly_monthly
|
87
|
+
extensions: []
|
88
|
+
extra_rdoc_files: []
|
89
|
+
files:
|
90
|
+
- ".codeclimate.yml"
|
91
|
+
- ".gitignore"
|
92
|
+
- ".rspec"
|
93
|
+
- ".rubocop.yml"
|
94
|
+
- ".travis.yml"
|
95
|
+
- Gemfile
|
96
|
+
- README.md
|
97
|
+
- Rakefile
|
98
|
+
- bin/daily_weekly_monthly
|
99
|
+
- daily_weekly_monthly.gemspec
|
100
|
+
- features/daily_weekly_monthly.feature
|
101
|
+
- features/step_definitions/daily_weekly_monthly_steps.rb
|
102
|
+
- features/support/env.rb
|
103
|
+
- lib/daily_weekly_monthly.rb
|
104
|
+
- lib/daily_weekly_monthly/cleaner.rb
|
105
|
+
- lib/daily_weekly_monthly/cli.rb
|
106
|
+
- lib/daily_weekly_monthly/creator.rb
|
107
|
+
- lib/daily_weekly_monthly/downloader.rb
|
108
|
+
- lib/daily_weekly_monthly/processor.rb
|
109
|
+
- lib/daily_weekly_monthly/runner.rb
|
110
|
+
- lib/daily_weekly_monthly/version.rb
|
111
|
+
- spec/daily_weekly_monthly/cleaner_spec.rb
|
112
|
+
- spec/daily_weekly_monthly/cli_spec.rb
|
113
|
+
- spec/daily_weekly_monthly/creator_spec.rb
|
114
|
+
- spec/daily_weekly_monthly/downloader_spec.rb
|
115
|
+
- spec/daily_weekly_monthly/processor_spec.rb
|
116
|
+
- spec/daily_weekly_monthly/runner_spec.rb
|
117
|
+
- spec/daily_weekly_monthly_spec.rb
|
118
|
+
- spec/spec_helper.rb
|
119
|
+
homepage: http://github.com/tpbowden/daily_weekly_monthly
|
120
|
+
licenses:
|
121
|
+
- MIT
|
122
|
+
metadata: {}
|
123
|
+
post_install_message:
|
124
|
+
rdoc_options: []
|
125
|
+
require_paths:
|
126
|
+
- lib
|
127
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: 2.0.0
|
132
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
133
|
+
requirements:
|
134
|
+
- - ">="
|
135
|
+
- !ruby/object:Gem::Version
|
136
|
+
version: '0'
|
137
|
+
requirements: []
|
138
|
+
rubyforge_project:
|
139
|
+
rubygems_version: 2.4.5
|
140
|
+
signing_key:
|
141
|
+
specification_version: 4
|
142
|
+
summary: Manage database backups
|
143
|
+
test_files: []
|
144
|
+
has_rdoc:
|