gitdis 0.0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (6) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +89 -0
  3. data/VERSION +1 -0
  4. data/bin/gitdis +88 -0
  5. data/lib/gitdis.rb +115 -0
  6. metadata +90 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: cbc3764ebe71bcc03b916fb6a29b1b02daaf34dd
4
+ data.tar.gz: 3dbd971bf31a576d535e176e573023fe362d383b
5
+ SHA512:
6
+ metadata.gz: 6da95b9acf0be41a12e4c588169d19ab10e72d48f381f4337fe229e166263b79d414be8cb2144cef37d6a9668de6771355bdf1ca1e28be4268372db013a26421
7
+ data.tar.gz: 64f87929af5d569b59bf08d2d927855ee657508fed9bd149ab37a577852062dfc4c8d94dfb972914a40fd032243298b6f21600088effeec78273d0f478700861
data/README.md ADDED
@@ -0,0 +1,89 @@
1
+ # gitdis
2
+
3
+ ## Install
4
+
5
+ ### Prerequisites
6
+
7
+ * Ruby (>= 2.0)
8
+
9
+ ### Procedure
10
+
11
+ `gem install gitdis`
12
+
13
+ ## Usage
14
+
15
+ `gitdis path/to/config.yaml [options]`
16
+
17
+ ## Config
18
+
19
+ ### YAML Config
20
+
21
+ 5 options, 1 keymap, plus environment overrides:
22
+
23
+ ```
24
+ redis-host: localhost
25
+ redis-port: 6379
26
+ redis-db: 0
27
+ git-repo: ~/foo/bar/baz
28
+ git-branch: quux
29
+ keymap:
30
+ "redis:key:1": "path/to/file1"
31
+ "redis:key:2": "path/to/file2"
32
+
33
+ environments:
34
+ yours:
35
+ redis-host: localhost
36
+ git-branch: mine
37
+ mine:
38
+ redis-host: localhost
39
+ git-branch: yours
40
+ qa:
41
+ redis-host: qa.big.com
42
+ git-branch: develop
43
+ prod:
44
+ redis-host: secret.big.net
45
+ git-branch: master
46
+ ```
47
+
48
+ ### Command Line Options
49
+
50
+ Select your environment (optional). Add final overrides (optional).
51
+
52
+ ```
53
+ Environment selection
54
+ -e, --environment select within YAML[environments]
55
+ Redis overrides
56
+ -H, --redis-host string
57
+ -p, --redis-port number
58
+ -d, --redis-db number
59
+ Git repo overrides
60
+ -r, --git-repo path/to/repo_dir
61
+ -b, --git-branch e.g. master
62
+ Other options
63
+ -D, --dump Just dump Redis contents per YAML keymap
64
+ -h, --help
65
+ ```
66
+
67
+ ### Basic operation
68
+
69
+ 1. pull the latest changes from origin on the specified branch
70
+ 2. iterate over all the expected filenames, skipping any that are missing
71
+ 3. calculate md5 for all the filenames
72
+ 4. compare the md5 to what is in redis
73
+ 5. update redis if the md5s do not match
74
+
75
+ ### Redis update
76
+
77
+ Assuming `foo:bar:baz` base key:
78
+
79
+ ```
80
+ # redis.connect(redis_options)
81
+ GET foo:bar:baz:md5 # assume md5 mismatch
82
+ SET foo:bar:baz # file contents
83
+ SET foo:bar:baz:md5 # file contents md5
84
+ INCR foo:bar:baz:version
85
+ ```
86
+
87
+ ### Execution
88
+
89
+ This script is short-running and intended to be scheduled by e.g. `cron`
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1.1
data/bin/gitdis ADDED
@@ -0,0 +1,88 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'yaml'
4
+ require 'slop'
5
+ require 'gitdis'
6
+
7
+ opts = Slop.parse { |o|
8
+ o.banner = "USAGE: gitdis path/to/config.yaml [options]"
9
+ o.separator ' Environment selection'
10
+ o.string '-e', '--environment', 'select within YAML[environments]'
11
+ o.separator ' Redis overrides'
12
+ o.string '-H', '--redis-host', 'string'
13
+ o.integer '-p', '--redis-port', 'number'
14
+ o.integer '-d', '--redis-db', 'number'
15
+ o.separator ' Git repo overrides'
16
+ o.string '-r', '--git-repo', 'path/to/repo_dir'
17
+ o.string '-b', '--git-branch', 'e.g. master'
18
+ o.separator ' Other options'
19
+ o.bool '-D', '--dump', 'Just dump Redis contents per YAML keymap'
20
+ o.on '-h', '--help' do
21
+ puts o
22
+ exit
23
+ end
24
+ }
25
+
26
+ unless(yaml_file = opts.arguments.shift)
27
+ puts opts
28
+ puts "path/to/config.yaml is required"
29
+ exit 1
30
+ end
31
+
32
+ # load the default (required) config
33
+ yaml = YAML.load_file(yaml_file)
34
+ config = yaml.fetch('default')
35
+
36
+ # apply any env-specific settings
37
+ if opts[:environment]
38
+ env = opts[:environment].downcase
39
+ unless yaml.key?(env)
40
+ warn "no environment #{env} found in #{yaml_file}"
41
+ exit 1
42
+ end
43
+ config.merge!(yaml[env])
44
+ end
45
+
46
+ # make sure we have a keymap
47
+ keymap = config.fetch('keymap')
48
+ raise "keymap should be a hash/map" unless keymap.is_a?(Hash)
49
+
50
+ # update config with any settings from opts
51
+ # create redis_options
52
+ redis_options = {}
53
+ %w[git-repo git-branch redis-host redis-port redis-db].each { |item|
54
+ opt_value = opts[item.to_sym]
55
+ config[item] = opt_value if opt_value
56
+
57
+ if item.match /^redis-/ # set :host, :port, :db
58
+ cfg_value = config[item]
59
+ if cfg_value
60
+ # "redis-foo-bar" => :foo_bar
61
+ ropt = item.split('-').drop(1).join('_').to_sym
62
+ redis_options[ropt] = cfg_value
63
+ end
64
+ end
65
+ puts [item, config[item]].join(': ') if config[item]
66
+ }
67
+ puts "Redis options: #{redis_options}"
68
+
69
+ # do things'n'stuff
70
+ if opts.dump?
71
+ GitDis.dump(keymap.keys, redis_options)
72
+ else
73
+ gd = GitDis.new config.fetch('git-repo')
74
+ gd.git_pull config.fetch('git-branch')
75
+ keymap.each { |key, fileglob|
76
+ result = gd.update(key, fileglob)
77
+ case result
78
+ when nil
79
+ puts "#{fileglob} not found"
80
+ when false
81
+ puts "#{fileglob} unchanged"
82
+ else
83
+ ver, md5 = *result
84
+ puts "#{fileglob} updated:"
85
+ puts "\tVersion: #{ver} (#{md5})"
86
+ end
87
+ }
88
+ end
data/lib/gitdis.rb ADDED
@@ -0,0 +1,115 @@
1
+ require 'open3' # used for rudimentary git pull
2
+ require 'digest' # md5sum
3
+ require 'redis'
4
+
5
+ class GitDis
6
+ # return Process::Status, stream through STDOUT and STDERR
7
+ def self.exec(cmd, bytes=1024)
8
+ Open3.popen3(cmd) { |sin, sout, serr, thr|
9
+ sin.close_write
10
+ while !sout.eof or !serr.eof
11
+ ready = IO.select [sout, serr]
12
+ if ready
13
+ ready[0].each { |f| # ready for reading
14
+ begin
15
+ (f == sout ? $stdout : $stderr).print f.read_nonblock(bytes)
16
+ rescue EOFError => e
17
+ # ok
18
+ end
19
+ }
20
+ end
21
+ end
22
+ thr.value # Process::Status
23
+ }
24
+ end
25
+
26
+ # raise on nonzero exit code
27
+ def self.exec!(cmd)
28
+ status = self.exec(cmd)
29
+ raise "`#{cmd}` #{status}" unless status.exitstatus == 0
30
+ 0
31
+ end
32
+
33
+ # file contents, version, md5
34
+ def self.keyset(base_key)
35
+ [base_key, [base_key, 'version'].join(':'), [base_key, 'md5'].join(':')]
36
+ end
37
+
38
+ def self.dump(keys, redis_options)
39
+ redis = Redis.new(redis_options)
40
+ keys.each { |base_key|
41
+ self.keyset(base_key).each { |rkey|
42
+ val = redis.get(rkey)
43
+ if val and val.include?("\n")
44
+ val = "\n" << val.split("\n").map { |line| "\t#{line}" }.join("\n")
45
+ end
46
+ puts ["[#{rkey}]", val].join(' ')
47
+ }
48
+ }
49
+ redis.disconnect
50
+ end
51
+
52
+ attr_accessor :repo_dir, :redis
53
+
54
+ def initialize(repo_dir, redis_options = {})
55
+ @repo_dir = File.expand_path(repo_dir)
56
+ raise "#{@repo_dir} does not exist!" unless Dir.exist? @repo_dir
57
+ @redis = Redis.new(redis_options)
58
+ end
59
+
60
+ def git_pull(git_branch)
61
+ Dir.chdir @repo_dir do
62
+ if self.class.exec("git diff --quiet HEAD").exitstatus != 0
63
+ raise "please stash your local changes"
64
+ end
65
+ self.class.exec! "git checkout #{git_branch}"
66
+ self.class.exec! "git pull"
67
+ end
68
+ self
69
+ end
70
+
71
+ # quick false if calculated md5 == redis md5
72
+ # otherwise update contents and md5; increment version
73
+ def update_redis(base_key, file_contents)
74
+ md5 = Digest::MD5.hexdigest(file_contents)
75
+ fkey, vkey, mkey = self.class.keyset(base_key)
76
+ return false if @redis.get(mkey) == md5
77
+
78
+ @redis.set(fkey, file_contents)
79
+ @redis.set(mkey, md5)
80
+ ver = @redis.incr(vkey)
81
+ [ver, md5]
82
+ end
83
+
84
+ # e.g. update('foo:bar:baz', 'foo/bar/*.baz')
85
+ # return nil # path does not exist
86
+ # false # no update needed
87
+ # [ver, md5] # updated
88
+ def update(base_key, relpath)
89
+ # handle e.g. "foo/bar/*.yaml"
90
+ files = Dir.glob(File.join(@repo_dir, relpath))
91
+ case files.length
92
+ when 0 then nil
93
+ when 1 then self.update_redis(base_key, File.read(files.first))
94
+ else
95
+ puts "concatenating #{files.length} files"
96
+ result = ''
97
+ sep = "\n"
98
+ files.each { |fname|
99
+ s = File.read(fname)
100
+ if s and !s.empty?
101
+ # scan for carriage returns (Microsoft text format)
102
+ sep = "\r\n" if sep == "\n" and s.include?("\r")
103
+ s << sep if s.last != "\n"
104
+ result << s
105
+ # debugging
106
+ elsif s
107
+ puts "#{fname} is empty"
108
+ else
109
+ puts "File.read(#{fname}) returned false/nil"
110
+ end
111
+ }
112
+ self.update_redis(base_key, result.chomp(sep))
113
+ end
114
+ end
115
+ end
metadata ADDED
@@ -0,0 +1,90 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gitdis
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Rick Hull
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-05-28 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: slop
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '4.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '4.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: redis
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '3.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: buildar
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '2'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '2'
55
+ description: Should you use this? YES!
56
+ email:
57
+ executables:
58
+ - gitdis
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - README.md
63
+ - VERSION
64
+ - bin/gitdis
65
+ - lib/gitdis.rb
66
+ homepage: https://github.com/rickhull/gitdis
67
+ licenses:
68
+ - MIT
69
+ metadata: {}
70
+ post_install_message:
71
+ rdoc_options: []
72
+ require_paths:
73
+ - lib
74
+ required_ruby_version: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: '2.0'
79
+ required_rubygems_version: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ requirements: []
85
+ rubyforge_project:
86
+ rubygems_version: 2.4.5
87
+ signing_key:
88
+ specification_version: 4
89
+ summary: Pull from git, push to redis and beyond!
90
+ test_files: []