modulesync 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 5b33cdfbc0df1b30570ffadbd78e6bbcec9ba8b6
4
+ data.tar.gz: 5044a6754a320ec00e0ebf4bb7cba71087da9e02
5
+ SHA512:
6
+ metadata.gz: 529d0ed35ec73c722cb607980133637851e1cfda52d564542ec3d0393bae5bcce62a5c72dff96b430654c2fff69ee263c17f4d3a9572924d3cd1342f89aacf74
7
+ data.tar.gz: 563cf2c60a67b435a9ff7481f37288236166ea1fa66d30087becf15ef932335ceda65685aecfd8d8625172a4594e8265a34ece2c2ab4d6fe555cf85099d78ddf
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ modules/
2
+ *.gem
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source ENV['GEM_SOURCE'] || 'https://rubygems.org'
2
+
3
+ gem 'git'
data/Gemfile.lock ADDED
@@ -0,0 +1,10 @@
1
+ GEM
2
+ remote: https://rubygems.org/
3
+ specs:
4
+ git (1.2.7)
5
+
6
+ PLATFORMS
7
+ ruby
8
+
9
+ DEPENDENCIES
10
+ git
data/LICENSE ADDED
@@ -0,0 +1,17 @@
1
+ Puppet Module Sync
2
+
3
+ Copyright (C) 2014 Puppet Labs Inc
4
+
5
+ Puppet Labs can be contacted at: info@puppetlabs.com
6
+
7
+ Licensed under the Apache License, Version 2.0 (the "License");
8
+ you may not use this file except in compliance with the License.
9
+ You may obtain a copy of the License at
10
+
11
+ http://www.apache.org/licenses/LICENSE-2.0
12
+
13
+ Unless required by applicable law or agreed to in writing, software
14
+ distributed under the License is distributed on an "AS IS" BASIS,
15
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ See the License for the specific language governing permissions and
17
+ limitations under the License.
data/README.md ADDED
@@ -0,0 +1,220 @@
1
+ ModuleSync
2
+ ===========
3
+
4
+ Table of Contents
5
+ -----------------
6
+
7
+ 1. [Usage TLDR](#usage-tldr)
8
+ 2. [Overview](#overview)
9
+ 3. [How it works](#how-it-works)
10
+ 4. [Installing](#installing)
11
+ 5. [Workflow](#workflow)
12
+ 6. [The Templates](#the-templates)
13
+
14
+ Usage TLDR
15
+ ----------
16
+
17
+ ```
18
+ gem install modulesync
19
+ msync --help
20
+ ```
21
+
22
+ Overview
23
+ --------
24
+
25
+ ModuleSync was written as a simple script with ERB templates to help the
26
+ Puppet Labs module engineers manage the zoo of Puppet modules on GitHub, and
27
+ has now been restructured and generalized to be used within other
28
+ organizations. Puppet modules within an organization tend to have a number of
29
+ meta-files that are identical or very similar between modules, such as the
30
+ Gemfile, .travis.yml, .gitignore, or spec\_helper.rb. If a file needs to
31
+ change in one module, it likely needs to change in the same way in every other
32
+ module that the organization manages.
33
+
34
+ One approach to this problem is to use sed in a bash for loop on the modules to
35
+ make a single change across every module. This approach falls short if there is
36
+ a single file that is purposefully different than the others, but still needs
37
+ to be managed. Moreover, this approach does not help if two files are
38
+ structured differently but need to be changed with the same meaning; for
39
+ instance, if the .travis.yml of one module uses a list of environments to
40
+ include, and another uses a matrix of includes with a list of excludes, adding
41
+ a test environment to both modules will require entirely different approaches.
42
+
43
+ ModuleSync provides the advantage of defining a standard template for each
44
+ file to follow, so it becomes clear what a file is supposed to look like. Two
45
+ files with the same semantics should also have the same syntax. A difference
46
+ between two files should have clear reasons, and old cruft should not be left
47
+ in files of one module as the files of another module march forward.
48
+
49
+ Another advantage of ModuleSync is the ability to run in no-op mode, which
50
+ makes local changes and shows the diffs, but does not make permanent changes in
51
+ the remote repository.
52
+
53
+ How It Works
54
+ ------------
55
+
56
+ ModuleSync is a gem that uses the GitHub workflow to clone, update, and push module
57
+ repositories. It expects to be activated from a directory containing
58
+ configuration for modulesync and the modules, or you can pass it the location
59
+ of this configuration directory. [The configuration for the Puppet Labs
60
+ modules](https://github.com/puppetlabs/modulesync\_configs), can be used as an
61
+ example for your own configuration. The configuration directory contains a
62
+ directory called moduleroot which mirrors the structure of a module. The files
63
+ in the moduleroot could be flat files or ERB templates. The templates are
64
+ rendered using values from a file called config\_defaults.yml in the root (not
65
+ moduleroot) of the configuration directory. The default values can be
66
+ overridden or extended by adding a file called .sync.yml to the module itself.
67
+ This allows us to, for example, have a set of "required" gems that are added
68
+ to all Gemfiles, and a set of "optional" gems that a single module might add.
69
+
70
+ The list of modules to manage is in managed\_modules.yml in the configuration
71
+ directory. This lists just the GitHub names of the modules to be managed.
72
+
73
+ ModuleSync can be called from the command line with parameters to change the
74
+ branch you're working on or the remote to clone from and push to. You can also
75
+ define these parameters in a file named modulesync.yml in the configuration
76
+ directory.
77
+
78
+ Installing
79
+ ----------
80
+
81
+ ```
82
+ gem install modulesync
83
+ ```
84
+
85
+ For developers:
86
+
87
+ ```
88
+ gem build modulesync.gemspec
89
+ gem install modulesync-*.gem
90
+ ```
91
+
92
+ Workflow
93
+ --------
94
+
95
+ ### Default mode
96
+
97
+ With no additional arguments, ModuleSync clones modules from the puppetlabs
98
+ github organization and pushes to the master branch.
99
+
100
+ #### Make changes
101
+
102
+ Make changes to a file in the moduleroot. For sanity's sake you should commit
103
+ and push these changes, but in this mode the update will be rendered from the
104
+ state of the files locally. Run `msync update` from the root of the
105
+ configuration directory (not moduleroot), or use -c <relative path> to point
106
+ it to the location of the configuration directory.
107
+
108
+ #### Dry-run
109
+
110
+ Do a dry-run to see what files will be changed, added and removed. This clones
111
+ the modules to `modules/<namespace>-<modulename>` in the current working, or if
112
+ the modules are already cloned, does an effective `git fetch origin; git
113
+ checkout master; git reset --hard origin/master` on the modules. Don't run
114
+ modulesync if the current working directory contains a modules/ directory with
115
+ changes you want to keep. The dry-run makes local changes there, but does not
116
+ commit or push changes. It is still destructive in that it overwrites local
117
+ changes.
118
+
119
+ ```
120
+ msync update --noop
121
+ ```
122
+
123
+ #### Damage mode
124
+
125
+ Make changes for real and push them back to master. This operates on the
126
+ pre-cloned modules from the dry-run or clones them fresh if the modules aren't
127
+ found.
128
+
129
+ ```
130
+ msync update
131
+ ```
132
+
133
+ #### Automating Updates
134
+
135
+ You can install a pre-push git hook to automatically clone, update, and push
136
+ modules upon pushing changes to the configuration directory. This does not
137
+ include a noop mode.
138
+
139
+ ```
140
+ msync hook activate
141
+ ```
142
+
143
+ If you have activated the hook but want to make changes to the configuration
144
+ directory (such as changes to managed_modules.yml or modulesync.yml) without
145
+ touching the modules, you can deactivate the hook.
146
+
147
+ ```
148
+ msync hook deactivate
149
+ ```
150
+
151
+ ### Using Forks and Non-master branches
152
+
153
+ The default functionality is to run ModuleSync on the puppetlabs modules, but
154
+ you can use this on your own organization's modules. This functionality also
155
+ applies if you want to work on a fork of the puppetlabs modules or work on a
156
+ non-master branch of any organization's modules. ModuleSync does not support
157
+ cloning from one remote and pushing to another, you are expected to fork
158
+ manually. It does not yet support automating pull requests (coming soon).
159
+
160
+ #### Dry-run
161
+
162
+ If you dry-run before doing the live update, you need to specify what namespace
163
+ to clone from because the live update will not re-clone if the modules are
164
+ already cloned. The namespace should be your fork, not the upstream module. The
165
+ format should be the SSH or HTTP prefix of the full URL minus the module name
166
+ itself.
167
+
168
+ ```
169
+ msync update -n git@github.com:puppetlabs --noop
170
+ ```
171
+
172
+ #### Damage mode
173
+
174
+ You don't technically need to specify the namespace if the modules are already
175
+ cloned from the dry-run, but it doesn't hurt. You do need to specify the
176
+ namespace if the modules are not pre-cloned. You need to specify a branch to
177
+ push to if you are not pushing to master.
178
+
179
+ ```
180
+ msync update -n git@github.com:puppetlabs -b sync_branch
181
+ ```
182
+
183
+ #### Configuring ModuleSync defaults
184
+
185
+ If you're not using the puppetlabs modules or only ever pushing to a fork of
186
+ them, then specifying the namespace and branch every time you use ModuleSync
187
+ probably seems excessive. You can create a file called modulesync.yml in the
188
+ configuration directory that provides these arguments automatically. This file
189
+ has a form such as:
190
+
191
+ ```
192
+ ---
193
+ namespace: mygithubusername
194
+ branch: modulesyncbranch
195
+ ```
196
+
197
+ Then you can run ModuleSync without extra arguments:
198
+
199
+ ```
200
+ msync update --noop
201
+ msync update
202
+ ```
203
+
204
+ #### Automating updates
205
+
206
+ If you install a git hook, you need to tell it what remote and branch to push
207
+ to. This may not work properly if you already have the modules cloned from a
208
+ different remote. The hook will also look in modulesync.yml for default
209
+ arguments.
210
+
211
+ ```
212
+ msync hook activate -n git@github.com:puppetlabs -b sync_branch
213
+ ```
214
+
215
+ The Templates
216
+ -------------
217
+
218
+ See the [modulesync\_configs](https://github.com/puppetlabs/modulesync_configs)
219
+ repository for an explanation of the templates that Puppet Labs uses on its
220
+ modules.
data/bin/msync ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ lib = File.expand_path('../../lib', __FILE__)
4
+ $:.unshift(lib) unless $:.include?(lib)
5
+
6
+ require 'modulesync'
7
+
8
+ ModuleSync.run(ARGV)
@@ -0,0 +1,81 @@
1
+ require 'optparse'
2
+ require 'modulesync/constants'
3
+ require 'modulesync/util'
4
+
5
+ module ModuleSync
6
+ class CLI
7
+ include Constants
8
+
9
+ def defaults
10
+ {
11
+ :namespace => 'puppetlabs',
12
+ :branch => 'master',
13
+ :managed_modules_conf => 'managed_modules.yml',
14
+ :configs => '.',
15
+ }
16
+ end
17
+
18
+ def commands_available
19
+ [
20
+ 'update',
21
+ 'hook',
22
+ ]
23
+ end
24
+
25
+ def fail(message)
26
+ puts @options[:help]
27
+ puts message
28
+ exit
29
+ end
30
+
31
+ def parse_opts(args)
32
+ @options = defaults
33
+ @options.merge!(Hash.transform_keys_to_symbols(Util.parse_config(MODULESYNC_CONF_FILE)))
34
+ @options[:command] = args[0] if commands_available.include?(args[0])
35
+ opt_parser = OptionParser.new do |opts|
36
+ opts.banner = "Usage: msync update [-m <commit message>] [-c <directory> ] [--noop] [-n <namespace>] [-b <branch>] | hook activate|deactivate [-c <directory> ] [-n <namespace>] [-b <branch>]"
37
+ opts.on('-m', '--message <msg>',
38
+ 'Commit message to apply to updated modules') do |msg|
39
+ @options[:message] = msg
40
+ end
41
+ opts.on('-n', '--namespace <url>',
42
+ 'Remote github namespace (user or organization) to clone from and push to. Defaults to puppetlabs') do |namespace|
43
+ @options[:namespace] = namespace
44
+ end
45
+ opts.on('-c', '--configs <directory>',
46
+ 'The local directory or remote repository to define the list of managed modules, the file templates, and the default values for template variables.') do |configs|
47
+ @options[:configs] = configs
48
+ end
49
+ opts.on('-b', '--branch <branch>',
50
+ 'Branch name to make the changes in. Defaults to "master"') do |branch|
51
+ @options[:branch] = branch
52
+ end
53
+ opts.on('--noop',
54
+ 'No-op mode') do |msg|
55
+ @options[:noop] = true
56
+ end
57
+ @options[:help] = opts.help
58
+ end.parse!
59
+
60
+ @options.fetch(:message) do
61
+ if @options[:command] == 'update' && ! @options[:noop]
62
+ fail("A commit message is required unless using noop.")
63
+ end
64
+ end
65
+
66
+ @options.fetch(:command) do
67
+ fail("A command is required.")
68
+ end
69
+
70
+ if @options[:command] == 'hook' &&
71
+ (! args.include?('activate') && ! args.include?('deactivate'))
72
+ fail("You must activate or deactivate the hook.")
73
+ end
74
+
75
+ end
76
+
77
+ def options
78
+ @options
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,11 @@
1
+ module ModuleSync
2
+ module Constants
3
+ MODULE_FILES_DIR = 'moduleroot/'
4
+ CONF_FILE = 'config_defaults.yml'
5
+ MODULE_CONF_FILE = '.sync.yml'
6
+ MODULESYNC_CONF_FILE = 'modulesync.yml'
7
+ PROJ_ROOT = './modules'
8
+ ENDPOINT = 'git@github.com'
9
+ HOOK_FILE = '.git/hooks/pre-push'
10
+ end
11
+ end
@@ -0,0 +1,81 @@
1
+ require 'git'
2
+
3
+ module ModuleSync
4
+ module Git
5
+ include Constants
6
+
7
+ def self.pull(org, name)
8
+ if ! Dir.exists?(PROJ_ROOT)
9
+ Dir.mkdir(PROJ_ROOT)
10
+ end
11
+
12
+ # Repo needs to be cloned in the cwd
13
+ if ! Dir.exists?("#{PROJ_ROOT}/#{name}") || ! Dir.exists?("#{PROJ_ROOT}/#{name}/.git")
14
+ puts "Cloning repository fresh"
15
+ remote = "#{ENDPOINT}:#{org}/#{name}.git"
16
+ local = "#{PROJ_ROOT}/#{name}"
17
+ repo = ::Git.clone(remote, local)
18
+
19
+ # Repo already cloned, check out master and override local changes
20
+ else
21
+ puts "Overriding any local changes to repositories in #{PROJ_ROOT}"
22
+ repo = ::Git.open("#{PROJ_ROOT}/#{name}")
23
+ repo.branch('master').checkout
24
+ repo.reset_hard
25
+ repo.pull
26
+ end
27
+ end
28
+
29
+ # Git add/rm, git commit, git push
30
+ def self.update(name, files, message, branch)
31
+ repo = ::Git.open("#{PROJ_ROOT}/#{name}")
32
+ repo.branch(branch).checkout
33
+ files.each do |file|
34
+ if repo.status.deleted.include?(file)
35
+ repo.remove(file)
36
+ else
37
+ repo.add(file)
38
+ end
39
+ end
40
+ begin
41
+ repo.commit(message)
42
+ #repo.push
43
+ rescue ::Git::GitExecuteError => git_error
44
+ if git_error.message.include? "nothing to commit, working directory clean"
45
+ puts "There were no files to update in #{name}. Not committing."
46
+ else
47
+ puts git_error
48
+ exit
49
+ end
50
+ end
51
+ end
52
+
53
+ # Needed because of a bug in the git gem that lists ignored files as
54
+ # untracked under some circumstances
55
+ # https://github.com/schacon/ruby-git/issues/130
56
+ def self.untracked_unignored_files(repo)
57
+ ignored = File.open("#{repo.dir.path}/.gitignore").read.split
58
+ repo.status.untracked.keep_if{|f,_| !ignored.any?{|i| f.match(/#{i}/)}}
59
+ end
60
+
61
+ def self.update_noop(name, branch)
62
+ puts "Using no-op. Files in #{name} may be changed but will not be committed."
63
+
64
+ repo = ::Git.open("#{PROJ_ROOT}/#{name}")
65
+ repo.branch(branch).checkout
66
+
67
+ puts "Files changed: "
68
+ repo.diff('HEAD', '--').each do |diff|
69
+ puts diff.patch
70
+ end
71
+
72
+ puts "Files added: "
73
+ untracked_unignored_files(repo).each do |file,_|
74
+ puts file
75
+ end
76
+
77
+ puts "\n\n"
78
+ puts '--------------------------------'
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,36 @@
1
+ module ModuleSync
2
+ module Hook
3
+ include Constants
4
+
5
+ def self.activate(args)
6
+ repo = args[:configs]
7
+ hook_args = ''
8
+ hook_args <<= " -n #{args[:namespace]}" if args[:namespace]
9
+ hook_args <<= " -b #{args[:branch]}" if args[:branch]
10
+ hook = <<EOF
11
+ #!/usr/bin/env bash
12
+
13
+ current_branch=\`git symbolic-ref HEAD | sed -e 's,.*/\(.*\),\1,'\`
14
+ git_dir=\`git rev-parse --show-toplevel\`
15
+ message=\`git log -1 --format=%B\`
16
+ msync -m "\$message"#{hook_args}
17
+ EOF
18
+ File.open("#{repo}/#{HOOK_FILE}", 'w') do |file|
19
+ file.write(hook)
20
+ end
21
+ end
22
+
23
+ def self.deactivate(repo)
24
+ hook_path = "#{repo}/#{HOOK_FILE}"
25
+ File.delete(hook_path) if File.exists?(hook_path)
26
+ end
27
+
28
+ def self.hook(command, args)
29
+ if (command == 'activate')
30
+ activate(args)
31
+ else
32
+ deactivate(args[:configs])
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,41 @@
1
+ require 'erb'
2
+ require 'find'
3
+
4
+ module ModuleSync
5
+ module Renderer
6
+
7
+ class ForgeModuleFile
8
+ def initialize(configs= {})
9
+ @configs = configs
10
+ end
11
+ end
12
+
13
+ def self.build(from_erb_template)
14
+ erb_obj = ERB.new(File.read(from_erb_template), nil, '-')
15
+ erb_obj.filename = from_erb_template.chomp('.erb')
16
+ erb_obj.def_method(ForgeModuleFile, 'render()')
17
+ erb_obj
18
+ end
19
+
20
+ def self.remove(file)
21
+ if File.exists?(file)
22
+ File.delete(file)
23
+ end
24
+ end
25
+
26
+ def self.render(template, configs = {})
27
+ ForgeModuleFile.new(configs).render()
28
+ end
29
+
30
+ def self.sync(template, to_file)
31
+ path = to_file.rpartition('/').first
32
+ if(! path.empty?)
33
+ FileUtils.mkdir_p(path)
34
+ end
35
+ File.open(to_file, 'w') do |file|
36
+ file.write(template)
37
+ end
38
+ end
39
+
40
+ end
41
+ end
@@ -0,0 +1,23 @@
1
+ require 'yaml'
2
+
3
+ module ModuleSync
4
+ module Util
5
+
6
+ def self.parse_config(config_file)
7
+ if File.exist?(config_file)
8
+ YAML.load_file(config_file) || {}
9
+ else
10
+ {}
11
+ end
12
+ end
13
+ end
14
+ end
15
+
16
+ class Hash
17
+ #take keys of hash and transform those to a symbols
18
+ def self.transform_keys_to_symbols(value)
19
+ return value if not value.is_a?(Hash)
20
+ hash = value.inject({}){|memo,(k,v)| memo[k.to_sym] = Hash.transform_keys_to_symbols(v); memo}
21
+ return hash
22
+ end
23
+ end
data/lib/modulesync.rb ADDED
@@ -0,0 +1,86 @@
1
+ require 'fileutils'
2
+ require 'modulesync/cli'
3
+ require 'modulesync/constants'
4
+ require 'modulesync/git'
5
+ require 'modulesync/hook'
6
+ require 'modulesync/renderer'
7
+ require 'modulesync/util'
8
+
9
+ module ModuleSync
10
+ include Constants
11
+
12
+ def self.local_file(config_path, file)
13
+ "#{config_path}/#{MODULE_FILES_DIR}/#{file}"
14
+ end
15
+
16
+ def self.module_file(puppet_module, file)
17
+ "#{PROJ_ROOT}/#{puppet_module}/#{file}"
18
+ end
19
+
20
+ def self.local_files(path)
21
+ if File.exists?(path)
22
+ local_files = Find.find(path).collect { |file| file if !File.directory?(file) }.compact
23
+ else
24
+ puts "#{path} does not exist. Check that you are working in your module configs directory or that you have passed in the correct directory with -c."
25
+ exit
26
+ end
27
+ end
28
+
29
+ def self.module_files(local_files, path)
30
+ local_files.map { |file| file.sub(/#{path}/, '') }
31
+ end
32
+
33
+ def self.managed_modules(path)
34
+ managed_modules = Util.parse_config(path)
35
+ if managed_modules.empty?
36
+ puts "No modules found at #{path}. Check that you specified the right configs directory containing managed_modules.yml."
37
+ exit
38
+ end
39
+ managed_modules
40
+ end
41
+
42
+ def self.run(args)
43
+ cli = CLI.new
44
+ cli.parse_opts(args)
45
+ options = cli.options
46
+ if options[:command] == 'update'
47
+ defaults = Util.parse_config("#{options[:configs]}/#{CONF_FILE}")
48
+
49
+ path = "#{options[:configs]}/#{MODULE_FILES_DIR}"
50
+ local_files = self.local_files(path)
51
+ module_files = self.module_files(local_files, path)
52
+
53
+ managed_modules = self.managed_modules("#{options[:configs]}/managed_modules.yml")
54
+
55
+ managed_modules.each do |puppet_module|
56
+ puts "Syncing #{puppet_module}"
57
+ Git.pull(options[:namespace], puppet_module)
58
+ module_configs = Util.parse_config("#{PROJ_ROOT}/#{puppet_module}/#{MODULE_CONF_FILE}")
59
+ files_to_manage = module_files | defaults.keys | module_configs.keys
60
+ files_to_delete = []
61
+ files_to_manage.each do |file|
62
+ file_configs = (defaults[file] || {}).merge(module_configs[file] || {})
63
+ if file_configs['unmanaged']
64
+ puts "Not managing #{file} in #{puppet_module}"
65
+ files_to_delete << file
66
+ elsif file_configs['delete']
67
+ Renderer.remove(module_file(puppet_module, file))
68
+ else
69
+ erb = Renderer.build(local_file(options[:configs], file))
70
+ template = Renderer.render(erb, file_configs)
71
+ Renderer.sync(template, "#{PROJ_ROOT}/#{puppet_module}/#{file}")
72
+ end
73
+ end
74
+ files_to_manage -= files_to_delete
75
+ if options[:noop]
76
+ Git.update_noop(puppet_module, options[:branch])
77
+ else
78
+ Git.update(puppet_module, files_to_manage, options[:message], options[:branch])
79
+ end
80
+ end
81
+ elsif options[:command] == 'hook'
82
+ Hook.hook(args[1], options)
83
+ end
84
+ end
85
+
86
+ end
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'modulesync'
7
+ spec.version = '0.0.1'
8
+ spec.authors = ['Colleen Murphy']
9
+ spec.email = ['colleen@puppetlabs.com']
10
+ spec.summary = %q{Puppet Module Synchronizer}
11
+ spec.description = %q{Utility to synchronize common files across puppet modules in Github.}
12
+ spec.homepage = "http://github.com/puppetlabs/modulesync"
13
+ spec.license = "Apache 2"
14
+
15
+ spec.files = `git ls-files -z`.split("\x0")
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_development_dependency "bundler", "~> 1.6"
21
+
22
+ spec.add_runtime_dependency 'git', '~>1.2'
23
+ end
metadata ADDED
@@ -0,0 +1,88 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: modulesync
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Colleen Murphy
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-09-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.6'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.6'
27
+ - !ruby/object:Gem::Dependency
28
+ name: git
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '1.2'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '1.2'
41
+ description: Utility to synchronize common files across puppet modules in Github.
42
+ email:
43
+ - colleen@puppetlabs.com
44
+ executables:
45
+ - msync
46
+ extensions: []
47
+ extra_rdoc_files: []
48
+ files:
49
+ - .gitignore
50
+ - Gemfile
51
+ - Gemfile.lock
52
+ - LICENSE
53
+ - README.md
54
+ - bin/msync
55
+ - lib/modulesync.rb
56
+ - lib/modulesync/cli.rb
57
+ - lib/modulesync/constants.rb
58
+ - lib/modulesync/git.rb
59
+ - lib/modulesync/hook.rb
60
+ - lib/modulesync/renderer.rb
61
+ - lib/modulesync/util.rb
62
+ - modulesync.gemspec
63
+ homepage: http://github.com/puppetlabs/modulesync
64
+ licenses:
65
+ - Apache 2
66
+ metadata: {}
67
+ post_install_message:
68
+ rdoc_options: []
69
+ require_paths:
70
+ - lib
71
+ required_ruby_version: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ required_rubygems_version: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - '>='
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
81
+ requirements: []
82
+ rubyforge_project:
83
+ rubygems_version: 2.2.2
84
+ signing_key:
85
+ specification_version: 4
86
+ summary: Puppet Module Synchronizer
87
+ test_files: []
88
+ has_rdoc: