gitomator 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 (41) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +57 -0
  3. data/.ruby-gemset +1 -0
  4. data/.ruby-version +1 -0
  5. data/Gemfile +13 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.md +194 -0
  8. data/Rakefile +9 -0
  9. data/bin/gitomator-clone-repos +21 -0
  10. data/bin/gitomator-console +19 -0
  11. data/bin/gitomator-disable-ci +24 -0
  12. data/bin/gitomator-enable-ci +24 -0
  13. data/bin/gitomator-make-access-permissions +72 -0
  14. data/bin/gitomator-make-repos +45 -0
  15. data/bin/gitomator-make-teams +20 -0
  16. data/bin/setup +8 -0
  17. data/gitomator.gemspec +28 -0
  18. data/lib/gitomator/console.rb +54 -0
  19. data/lib/gitomator/context.rb +132 -0
  20. data/lib/gitomator/exceptions.rb +21 -0
  21. data/lib/gitomator/service/ci.rb +35 -0
  22. data/lib/gitomator/service/git.rb +48 -0
  23. data/lib/gitomator/service/hosting.rb +204 -0
  24. data/lib/gitomator/service/tagging.rb +99 -0
  25. data/lib/gitomator/service.rb +64 -0
  26. data/lib/gitomator/service_provider/git_shell.rb +68 -0
  27. data/lib/gitomator/service_provider/hosting_local.rb +103 -0
  28. data/lib/gitomator/task/base_repos_task.rb +88 -0
  29. data/lib/gitomator/task/clone_repos.rb +61 -0
  30. data/lib/gitomator/task/config/repos_config.rb +117 -0
  31. data/lib/gitomator/task/config/team_config.rb +57 -0
  32. data/lib/gitomator/task/enable_disable_ci.rb +56 -0
  33. data/lib/gitomator/task/make_repos.rb +86 -0
  34. data/lib/gitomator/task/setup_team.rb +63 -0
  35. data/lib/gitomator/task/update_repo_access_permissions.rb +42 -0
  36. data/lib/gitomator/task.rb +48 -0
  37. data/lib/gitomator/util/repo/name_resolver.rb +68 -0
  38. data/lib/gitomator/util/script_util.rb +69 -0
  39. data/lib/gitomator/version.rb +3 -0
  40. data/lib/gitomator.rb +61 -0
  41. metadata +173 -0
@@ -0,0 +1,132 @@
1
+ require 'gitomator'
2
+
3
+
4
+ module Gitomator
5
+
6
+
7
+ #
8
+ # Register service-factories, and create services.
9
+ #
10
+ class BaseContext
11
+
12
+ #
13
+ # @param config [Hash]
14
+ #
15
+ def initialize(config)
16
+ @config = config
17
+ @service2factory = {}
18
+ @services = {}
19
+
20
+ config.select { |_,v| v.is_a?(Hash) && v.has_key?('provider')}
21
+ .each do |service, service_config|
22
+ register_service(service.to_sym) do |config|
23
+ self.send("create_#{config['provider']}_#{service}_service", config || {})
24
+ end
25
+ end
26
+
27
+ end
28
+
29
+
30
+ #
31
+ # @param service [Symbol] The service's name.
32
+ # @param block [Proc<Hash> -> Object] Given a config, create a service object.
33
+ #
34
+ def register_service(service, &block)
35
+ @service2factory[service] = block
36
+
37
+ # Create a lazy-loader getter for the service
38
+ unless self.respond_to? service
39
+ self.class.send(:define_method, service) do
40
+ @services[service] ||= @service2factory[service].call(@config[service.to_s] || {})
41
+ end
42
+ end
43
+ end
44
+
45
+ end
46
+
47
+
48
+ #=============================================================================
49
+
50
+
51
+ class Context < BaseContext
52
+
53
+ #
54
+ # Convenience function to create Context instances from configuration files.
55
+ # @param config_file [String/File] - YAML configuration file.
56
+ #
57
+ def self.from_file(config_file)
58
+ return new(Gitomator::Util.load_config(config_file))
59
+ end
60
+
61
+ #---------------------------------------------------------------------------
62
+
63
+
64
+ DEFAULT_CONFIG = {
65
+ 'git' => { 'provider' => 'shell'},
66
+ 'hosting' => { 'provider' => 'local'}
67
+ }
68
+
69
+ def initialize(config={})
70
+ super(DEFAULT_CONFIG.merge(config))
71
+ end
72
+
73
+
74
+ def create_local_hosting_service(config)
75
+ require 'gitomator/service/hosting'
76
+ require 'gitomator/service_provider/hosting_local'
77
+ require 'tmpdir'
78
+
79
+ dir = config['dir'] || Dir.mktmpdir('Gitomator_')
80
+ return Gitomator::Service::Hosting.new (
81
+ Gitomator::ServiceProvider::HostingLocal.new(git, dir)
82
+ )
83
+ end
84
+
85
+
86
+ def create_shell_git_service(_)
87
+ require 'gitomator/service/git'
88
+ require 'gitomator/service_provider/git_shell'
89
+ Gitomator::Service::Git.new(Gitomator::ServiceProvider::GitShell.new())
90
+ end
91
+
92
+
93
+ #---------------------------------------------------------------------------
94
+
95
+ # Services from here onwards should have really been plug-ins.
96
+ # Unfortuantely, I don't know of a clean way to do that in Ruby.
97
+
98
+
99
+ def create_github_hosting_service(config)
100
+ require 'gitomator/service/hosting'
101
+ require 'gitomator/github/hosting_provider'
102
+
103
+ return Gitomator::Service::Hosting.new (
104
+ Gitomator::GitHub::HostingProvider.from_config(config))
105
+ end
106
+
107
+
108
+ def create_travis_ci_service(config)
109
+ require 'gitomator/service/ci'
110
+ require 'gitomator/travis/ci_provider'
111
+
112
+ return Gitomator::Service::CI.new(
113
+ Gitomator::Travis::CIProvider.from_config(config))
114
+ end
115
+
116
+ def create_travis_pro_ci_service(config)
117
+ create_travis_ci_service(config)
118
+ end
119
+
120
+
121
+ def create_github_tagging_service(config)
122
+ require 'gitomator/service/tagging'
123
+ require 'gitomator/github/tagging_provider'
124
+
125
+ return Gitomator::Service::Tagging.new (
126
+ Gitomator::GitHub::TaggingProvider.from_config(config))
127
+ end
128
+
129
+
130
+
131
+ end
132
+ end
@@ -0,0 +1,21 @@
1
+ module Gitomator
2
+ module Exception
3
+
4
+ #
5
+ # This error will be thrown when a valid service method is called, but
6
+ # the underlying provider does not implement the given method.
7
+ #
8
+ class UnsupportedProviderMethod < StandardError
9
+ attr_reader :provider, :method
10
+
11
+ def initialize(provider, method)
12
+ @provider = provider
13
+ @method = method
14
+
15
+ provider_name = provider.respond_to?(:name) ? provider.name : 'Unknown'
16
+ super("#{provider_name} provider does not support the #{method} method.")
17
+ end
18
+ end
19
+
20
+ end
21
+ end
@@ -0,0 +1,35 @@
1
+ require 'gitomator/service'
2
+
3
+ module Gitomator
4
+ module Service
5
+ class CI < Gitomator::BaseService
6
+
7
+
8
+ def initialize(provider, opts = {})
9
+ super(provider, opts)
10
+ end
11
+
12
+
13
+ def sync(blocking=false, opts={})
14
+ service_call(__callee__, blocking, opts)
15
+ end
16
+
17
+ def syncing?(opts={})
18
+ service_call(__callee__, opts)
19
+ end
20
+
21
+ def enable_ci(repo, opts={})
22
+ service_call(__callee__, repo, opts)
23
+ end
24
+
25
+ def disable_ci(repo, opts={})
26
+ service_call(__callee__, repo, opts)
27
+ end
28
+
29
+ def ci_enabled?(repo)
30
+ service_call(__callee__, repo)
31
+ end
32
+
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,48 @@
1
+ require 'gitomator/service'
2
+
3
+ module Gitomator
4
+ module Service
5
+ class Git < Gitomator::BaseService
6
+
7
+
8
+ def initialize(provider, opts = {})
9
+ super(provider, opts)
10
+ end
11
+
12
+ # ----------------------------------------------------------------------
13
+
14
+ def clone(repo_url, local_repo_root, opts={})
15
+ service_call(__callee__, repo_url, local_repo_root, opts)
16
+ end
17
+
18
+ def init(local_repo_root, opts={})
19
+ service_call(__callee__, local_repo_root, opts)
20
+ end
21
+
22
+ def add(local_repo_root, path, opts={})
23
+ service_call(__callee__, local_repo_root, path, opts)
24
+ end
25
+
26
+ def commit(local_repo_root, message, opts={})
27
+ service_call(__callee__, local_repo_root, message, opts)
28
+ end
29
+
30
+ def checkout(local_repo_root, branch, opts={})
31
+ service_call(__callee__, local_repo_root, branch, opts)
32
+ end
33
+
34
+ def set_remote(local_repo_root, remote, url, opts={})
35
+ service_call(__callee__, local_repo_root, remote, url, opts)
36
+ end
37
+
38
+ def push(local_repo_root, remote, opts={})
39
+ service_call(__callee__, local_repo_root, remote, opts)
40
+ end
41
+
42
+ def command(local_repo_root, command)
43
+ service_call(__callee__, local_repo_root, command)
44
+ end
45
+
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,204 @@
1
+ require 'gitomator/service'
2
+ require 'gitomator/util/repo/name_resolver'
3
+
4
+ module Gitomator
5
+ module Service
6
+ class Hosting < Gitomator::BaseService
7
+
8
+
9
+ def initialize(provider, opts = {})
10
+ super(provider, opts)
11
+ @name_resolver = Gitomator::Util::Repo::NameResolver.new(
12
+ provider.respond_to?(:namespace) ? provider.namespace : nil)
13
+ end
14
+
15
+ # ---------------------------- Name resolution ---------------------------
16
+
17
+ def resolve_namespace(name)
18
+ @name_resolver.namespace(name)
19
+ end
20
+
21
+ def resolve_repo_name(name)
22
+ @name_resolver.name_only(name)
23
+ end
24
+
25
+ def resolve_branch(name)
26
+ @name_resolver.branch(name)
27
+ end
28
+
29
+ # ----------------------- CRUD operations on repos -----------------------
30
+
31
+ def create_repo(name, opts={})
32
+ service_call(__callee__, name, opts)
33
+ end
34
+
35
+ def read_repo(name)
36
+ service_call(__callee__, name)
37
+ end
38
+
39
+ def update_repo(name, opts={})
40
+ service_call(__callee__, name, opts)
41
+ end
42
+
43
+ def delete_repo(name)
44
+ service_call(__callee__, name)
45
+ end
46
+
47
+ def search_repos(query, opts={})
48
+ service_call(__callee__, query, opts)
49
+ end
50
+
51
+ # ----------------------- CRUD operations on teams ---------------------
52
+
53
+ def create_team(name, opts={})
54
+ service_call(__callee__, name, opts)
55
+ end
56
+
57
+ def read_team(name)
58
+ service_call(__callee__, name)
59
+ end
60
+
61
+ def update_team(name, opts={})
62
+ service_call(__callee__, name, opts)
63
+ end
64
+
65
+ def delete_team(name)
66
+ service_call(__callee__, name)
67
+ end
68
+
69
+ def search_teams(query, opts={})
70
+ service_call(__callee__, query, opts)
71
+ end
72
+
73
+ # ----------------------------------------------------------------------
74
+
75
+ def search_users(query, opts={})
76
+ service_call(__callee__, query, opts)
77
+ end
78
+
79
+ # ------------ CRUD operations on team_memberships -----------------------
80
+
81
+
82
+ #
83
+ # @param team_name [String]
84
+ # @param user_name [String]
85
+ # @param role [String]
86
+ #
87
+ def create_team_membership(team_name, user_name, role=nil)
88
+ # The if/else block is here so that different providers can implement
89
+ # the `create_team_membership` with a default value for the role argument.
90
+ if role.nil?
91
+ service_call(__callee__, team_name, user_name)
92
+ else
93
+ service_call(__callee__, team_name, user_name, role)
94
+ end
95
+
96
+ end
97
+
98
+ #
99
+ # @param team_name [String]
100
+ # @param user_name [String]
101
+ #
102
+ # @return [String] The current role of `user_name` in `team_name`, or nil (if the user is not a member of the team)
103
+ #
104
+ def read_team_membership(team_name, user_name)
105
+ service_call(__callee__, team_name, user_name)
106
+ end
107
+
108
+ #
109
+ # @param team_name [String]
110
+ # @param user_name [String]
111
+ # @param role [String]
112
+ #
113
+ def update_team_membership(team_name, user_name, role)
114
+ service_call(__callee__, team_name, user_name, role)
115
+ end
116
+
117
+ #
118
+ # @param team_name [String]
119
+ # @param user_name [String]
120
+ #
121
+ def delete_team_membership(team_name, user_name)
122
+ service_call(__callee__, team_name, user_name)
123
+ end
124
+
125
+ # --------------- CRUD operations on permissions -----------------------
126
+
127
+ #
128
+ # @param user (String) username
129
+ # @param repo (String) Repo name (full name, or repo-only)
130
+ # @param permission (Symbol) Either :read or :write or nil
131
+ #
132
+ def set_user_permission(user, repo, permission)
133
+ service_call(__callee__, user, repo, permission)
134
+ end
135
+
136
+ #
137
+ # @param user (String) username
138
+ # @param team (String) Team name
139
+ # @param permission (Symbol) Either :read or :write or nil
140
+ #
141
+ def set_team_permission(team, repo, permission)
142
+ service_call(__callee__, team, repo, permission)
143
+ end
144
+
145
+ # ----------------------------------------------------------------------
146
+
147
+
148
+ # ------------------- CRUD operations on pull-requests -----------------
149
+ #
150
+ # We assume that pull-requests have id's, and that id's are
151
+ # unique within a repo.
152
+ # That is, the pair (dest_repo, id) uniquely identifies a pull-request.
153
+ #
154
+
155
+
156
+
157
+ def create_pull_request(src, dst, opts = {})
158
+ service_call(__callee__, src, dst, opts)
159
+ end
160
+
161
+
162
+
163
+ def read_pull_request(dst_repo, id)
164
+ service_call(__callee__, dst_repo, id)
165
+ end
166
+
167
+ def read_pull_requests(dst_repo, opts = {})
168
+ service_call(__callee__, dst_repo, opts)
169
+ end
170
+
171
+
172
+ # Pull-request update methods:
173
+
174
+
175
+ def merge_pull_request(dst_repo, id, message='')
176
+ service_call(__callee__, dst_repo, id, message)
177
+ end
178
+
179
+ def close_pull_request(dst_repo, id)
180
+ service_call(__callee__, dst_repo, id)
181
+ end
182
+
183
+ def open_pull_request(dst_repo, id)
184
+ service_call(__callee__, dst_repo, id)
185
+ end
186
+
187
+
188
+ #
189
+ # A "general-purpose" update method for pull-requests.
190
+ #
191
+ def update_pull_request(dst_repo, id, opts)
192
+ service_call(__callee__, dst_repo, id)
193
+ end
194
+
195
+
196
+
197
+ def delete_pull_request(dst_repo, id)
198
+ service_call(__callee__, dst_repo, id)
199
+ end
200
+
201
+
202
+ end
203
+ end
204
+ end
@@ -0,0 +1,99 @@
1
+ require 'gitomator/service'
2
+
3
+ module Gitomator
4
+ module Service
5
+ class Tagging < Gitomator::BaseService
6
+
7
+
8
+ #
9
+ # Add `tag` to object (the object with the given `id_or_name` in the
10
+ # given `namespace`)
11
+ #
12
+ # @param namespace [String]
13
+ # @param id_or_name [String/Number]
14
+ # @param tags [*String]
15
+ #
16
+ def add_tags(namespace, id_or_name, *tags)
17
+ service_call(__callee__, namespace, id_or_name, *tags)
18
+ end
19
+
20
+
21
+ #
22
+ # Remove tag from object.
23
+ #
24
+ # @param namespace [String]
25
+ # @param id_or_name [String/Number]
26
+ # @param tag [String]
27
+ #
28
+ def remove_tag(namespace, id_or_name, tag)
29
+ service_call(__callee__, namespace, id_or_name, tag)
30
+ end
31
+
32
+
33
+ #
34
+ # Get all tags associated with the specified object.
35
+ #
36
+ # @param namespace [String]
37
+ # @param id_or_name [String/Number]
38
+ #
39
+ # @return [Array<String>]
40
+ #
41
+ def tags(namespace, id_or_name)
42
+ service_call(__callee__, namespace, id_or_name)
43
+ end
44
+
45
+
46
+ #
47
+ # Search for objects by tag(s).
48
+ #
49
+ # @param namespace [String]
50
+ # @param query [String/Hash] - Either a single tag (String) or a query (Hash).
51
+ #
52
+ # @return Enumerable of object identifiers.
53
+ #
54
+ def search(namespace, query)
55
+ service_call(__callee__, namespace, query)
56
+ end
57
+
58
+
59
+ #
60
+ # Get the metadata associated with the given tag in the given namespace.
61
+ # If a `tag` is not specified, get metadata for all tags in the namespace.
62
+ #
63
+ # @param namespace [String]
64
+ # @param tag [String]
65
+ #
66
+ # @return [Hash<String,Object>] The tag's metadata. If no tag was specified, return a Hash that maps each tag to its metadata.
67
+ #
68
+ def metadata(namespace, tag=nil)
69
+ service_call(__callee__, namespace, tag)
70
+ end
71
+
72
+
73
+ #
74
+ # Add the given metadata to the given tag (in the given namespace).
75
+ # You can remove metadata properties by updating their value to nil.
76
+ #
77
+ # @param namespace [String]
78
+ # @param tag [String]
79
+ # @param metadata [String]
80
+ #
81
+ def set_metadata(namespace, tag, metadata)
82
+ service_call(__callee__, namespace, tag, metadata)
83
+ end
84
+
85
+
86
+ #
87
+ # Delete all metadata associated with the given tag (in the given namespace).
88
+ #
89
+ # @param namespace [String]
90
+ # @param tag [String]
91
+ #
92
+ def delete_metadata(namespace, tag)
93
+ service_call(__callee__, namespace, tag)
94
+ end
95
+
96
+
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,64 @@
1
+ require 'gitomator'
2
+ require 'gitomator/exceptions'
3
+
4
+ module Gitomator
5
+ class BaseService
6
+
7
+ attr_reader :provider
8
+
9
+ def initialize(provider, opts = {})
10
+ @provider = provider
11
+ @blocks = { :before => [], :after => [], :error => [] }
12
+ end
13
+
14
+ #---------------------------------------------------------------------------
15
+ # Inject blocks that do pre/post processing (e.g. logging)
16
+ # Note: The blocks are bound to the service instance (i.e. in the block,
17
+ # self refers to the service instance)
18
+
19
+ #
20
+ # @param block [ Proc (String method_name, Array<Object> args) => nil ]
21
+ #
22
+ def before_each_service_call(&block)
23
+ @blocks[:before].push block
24
+ end
25
+
26
+ #
27
+ # @param block [ Proc (String method_name, Array<Object> args, Object result) => nil ]
28
+ #
29
+ def after_each_service_call(&block)
30
+ @blocks[:after].push block
31
+ end
32
+
33
+ #
34
+ # @param block [ Proc (String method_name, Array<Object> args, Error error) => nil ]
35
+ #
36
+ def on_service_call_error(&block)
37
+ @blocks[:error].push block
38
+ end
39
+
40
+ #---------------------------------------------------------------------------
41
+
42
+
43
+ def service_call(method, *args)
44
+ result = nil
45
+ @blocks[:before].each {|block| self.instance_exec(method, args, &block) }
46
+
47
+ begin
48
+ if provider.respond_to?(method)
49
+ result = @provider.send(method, *args)
50
+ else
51
+ raise Gitomator::Exception::UnsupportedProviderMethod.new(@provider, method)
52
+ end
53
+ rescue Exception => e
54
+ @blocks[:error].each {|block| self.instance_exec(method, args, e, &block) }
55
+ raise e
56
+ end
57
+
58
+ @blocks[:after].each {|block| self.instance_exec(method, args, result, &block) }
59
+ return result
60
+ end
61
+
62
+
63
+ end
64
+ end
@@ -0,0 +1,68 @@
1
+ module Gitomator
2
+ module ServiceProvider
3
+ class GitShell
4
+
5
+ def clone(repo_url, local_repo_root, opts)
6
+ _run_command("git clone #{repo_url} #{local_repo_root}", opts = {})
7
+ end
8
+
9
+ def init(local_repo_root, opts = {})
10
+ Dir.mkdir(local_repo_root) unless Dir.exists?(local_repo_root)
11
+ _run_git_command("init", local_repo_root)
12
+ end
13
+
14
+ def add(local_repo_root, path, opts={})
15
+ _run_git_command("add #{path}", local_repo_root, opts)
16
+ end
17
+
18
+ def commit(local_repo_root, message, opts={})
19
+ cmd = "commit -m \"#{message.gsub('"', '\\\"')}\""
20
+ _run_git_command(cmd, local_repo_root, opts)
21
+ end
22
+
23
+ def checkout(local_repo_root, branch, opts)
24
+ cmd = 'checkout '
25
+ if opts[:is_new]
26
+ cmd += '-b '
27
+ end
28
+ cmd += branch
29
+ if opts[:is_remote]
30
+ cmd += " origin/#{branch}"
31
+ end
32
+
33
+ _run_git_command(cmd, local_repo_root, {})
34
+ end
35
+
36
+ def set_remote(local_repo_root, remote, url, opts)
37
+ cmd = "remote #{opts[:create] ? 'add' : 'set-url'} #{remote} #{url}"
38
+ opts.delete :create
39
+ _run_git_command(cmd, local_repo_root, opts)
40
+ end
41
+
42
+ def push(local_repo_root, remote, opts)
43
+ raise "Unsupported"
44
+ end
45
+
46
+
47
+ def command(local_repo_root, command)
48
+ _run_git_command(command, local_repo_root, opts = {})
49
+ end
50
+
51
+ #=====================================================================
52
+
53
+
54
+ def _run_command(cmd, opts = {})
55
+ system({}, cmd, opts)
56
+ end
57
+
58
+ def _run_git_command(cmd, local_repo_root, opts = {})
59
+ opts[:chdir] = local_repo_root
60
+ unless cmd.strip.start_with? 'git'
61
+ cmd = 'git ' + cmd
62
+ end
63
+ _run_command(cmd, opts)
64
+ end
65
+
66
+ end
67
+ end
68
+ end