gitdis 0.0.1.1

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.
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: []