txgh 6.8.1 → 7.0.0.beta

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/lib/txgh/config/config_pair.rb +12 -4
  3. data/lib/txgh/config/key_manager.rb +4 -6
  4. data/lib/txgh/config/providers/git_provider.rb +5 -7
  5. data/lib/txgh/config/providers/github_provider.rb +16 -0
  6. data/lib/txgh/config/providers/gitlab_provider.rb +16 -0
  7. data/lib/txgh/config/providers.rb +5 -3
  8. data/lib/txgh/config/tx_manager.rb +2 -2
  9. data/lib/txgh/git_api.rb +85 -0
  10. data/lib/txgh/git_repo.rb +90 -0
  11. data/lib/txgh/git_status.rb +151 -0
  12. data/lib/txgh/github_api.rb +1 -13
  13. data/lib/txgh/github_repo.rb +1 -89
  14. data/lib/txgh/github_status.rb +1 -148
  15. data/lib/txgh/gitlab_api.rb +63 -0
  16. data/lib/txgh/gitlab_repo.rb +4 -0
  17. data/lib/txgh/gitlab_status.rb +4 -0
  18. data/lib/txgh/version.rb +1 -1
  19. data/lib/txgh.rb +10 -3
  20. data/spec/category_support_spec.rb +6 -8
  21. data/spec/config/config_pair_spec.rb +41 -16
  22. data/spec/config/key_manager_spec.rb +30 -17
  23. data/spec/config/provider_instance_spec.rb +3 -6
  24. data/spec/config/provider_support_spec.rb +2 -5
  25. data/spec/config/tx_config_spec.rb +2 -4
  26. data/spec/config/tx_manager_spec.rb +8 -11
  27. data/spec/diff_calculator_spec.rb +2 -4
  28. data/spec/events_spec.rb +2 -4
  29. data/spec/github_api_spec.rb +2 -4
  30. data/spec/github_repo_spec.rb +10 -16
  31. data/spec/github_status_spec.rb +5 -7
  32. data/spec/gitlab_api_spec.rb +112 -0
  33. data/spec/gitlab_repo_spec.rb +172 -0
  34. data/spec/gitlab_status_spec.rb +93 -0
  35. data/spec/helpers/standard_txgh_setup.rb +32 -6
  36. data/spec/merge_calculator_spec.rb +4 -6
  37. data/spec/parse_config_spec.rb +3 -5
  38. data/spec/resource_committer_spec.rb +3 -5
  39. data/spec/resource_contents_spec.rb +8 -10
  40. data/spec/resource_deleter_spec.rb +3 -5
  41. data/spec/resource_downloader_spec.rb +2 -4
  42. data/spec/resource_updater_spec.rb +3 -5
  43. data/spec/transifex_api_spec.rb +2 -4
  44. data/spec/tx_branch_resource_spec.rb +10 -12
  45. data/spec/tx_resource_spec.rb +2 -4
  46. data/spec/utils_spec.rb +22 -24
  47. data/txgh.gemspec +1 -1
  48. metadata +29 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 866a82620ffdca5577a209cb300c898893c1acbf1007f3d7edcfdfd5ed35e527
4
- data.tar.gz: 5613e6d811c0d232b5349f8d47443b30807f2a365598b7b187e08464e2849ff4
3
+ metadata.gz: 1a713ede4978e35c714413a0ac32b7370f3b38e4409078434507d3f95740c9b8
4
+ data.tar.gz: edbc8a76d3e258a82026ffd49e9ff68933064b4d77e10ded0bb3f2785580aae8
5
5
  SHA512:
6
- metadata.gz: 8268cbbbc4275285b0498f46d834932a84fd96e7242ba14057fdcc23a5392a4c6e75289cd599297d3082a6cee4b106ff2ae3a58484cabdd4dd1216b1f539c883
7
- data.tar.gz: 0b1e23f4ee6f7595264e3a481f9a233ea22b8bc2df519b9b7ba54f8877f41777846157462fbd432f5155bab191c2f4855b1bd62f07700f2800fba72a44778cf4
6
+ metadata.gz: 6245e7d48ba1cd1365de1b5fc9a5864f75a030c5bcd9e8b8f550abb3429f5ae956e69ad9b82d3701890800a4a9d5027509b4fb0feca5567ae11a1f14e7ae1a2b
7
+ data.tar.gz: f0d642665e13844b4adf43dc3f0ec41629f1587215c0fcaacc5cb3add64d13e944de99f547dcb140f3dc3e7e45e57b23e7f14e27da9a65530cd42b330c36c1e7
@@ -8,10 +8,12 @@ module Txgh
8
8
  @repo_config = repo_config
9
9
  end
10
10
 
11
- def github_repo
12
- @github_repo ||= Txgh::GithubRepo.new(
13
- repo_config, github_api
14
- )
11
+ def git_repo
12
+ @git_repo ||= if @repo_config['git_repo_source'] == 'gitlab'
13
+ Txgh::GitlabRepo.new(repo_config, gitlab_api)
14
+ else
15
+ Txgh::GithubRepo.new(repo_config, github_api)
16
+ end
15
17
  end
16
18
 
17
19
  def transifex_project
@@ -31,6 +33,12 @@ module Txgh
31
33
  repo_config['api_username'], repo_config['api_token'], repo_config['name']
32
34
  )
33
35
  end
36
+
37
+ def gitlab_api
38
+ @gitlab_api ||= Txgh::GitlabApi.create_from_credentials(
39
+ nil, repo_config['api_token'], repo_config['name']
40
+ )
41
+ end
34
42
  end
35
43
  end
36
44
  end
@@ -29,10 +29,6 @@ module Txgh
29
29
  base_config['transifex']['projects'].keys
30
30
  end
31
31
 
32
- def repo_names
33
- base_config['github']['repos'].keys
34
- end
35
-
36
32
  private
37
33
 
38
34
  def raw_config
@@ -63,8 +59,10 @@ module Txgh
63
59
  end
64
60
 
65
61
  def repo_config_for(repo_name)
66
- if config = base_config['github']['repos'][repo_name]
67
- config.merge('name' => repo_name)
62
+ if config = base_config.dig('github', 'repos', repo_name)
63
+ config.merge('name' => repo_name, 'git_repo_source' => 'github')
64
+ elsif config = base_config.dig('gitlab', 'repos', repo_name)
65
+ config.merge('name' => repo_name, 'git_repo_source' => 'gitlab')
68
66
  else
69
67
  raise Txgh::RepoConfigNotFoundError,
70
68
  "Couldn't find any configuration for the '#{repo_name}' repo."
@@ -24,7 +24,7 @@ module Txgh
24
24
  @payload = payload
25
25
  @parser = parser
26
26
  @ref = options[:ref]
27
- @github_repo = options[:github_repo]
27
+ @git_repo = options[:git_repo]
28
28
  end
29
29
 
30
30
  def config
@@ -34,9 +34,7 @@ module Txgh
34
34
  private
35
35
 
36
36
  def download
37
- github_repo.api.download(payload, ref)[:content]
38
- rescue Octokit::NotFound
39
- raise Txgh::GitConfigNotFoundError, "Config file #{payload} not found in #{ref}"
37
+ raise NotImplementedError
40
38
  end
41
39
 
42
40
  def ref
@@ -48,13 +46,13 @@ module Txgh
48
46
  @ref
49
47
  end
50
48
 
51
- def github_repo
52
- unless @github_repo
49
+ def git_repo
50
+ unless @git_repo
53
51
  raise TxghError,
54
52
  "TX_CONFIG specified a file from git but did not provide a repo."
55
53
  end
56
54
 
57
- @github_repo
55
+ @git_repo
58
56
  end
59
57
  end
60
58
  end
@@ -0,0 +1,16 @@
1
+ module Txgh
2
+ module Config
3
+ module Providers
4
+ class GithubProvider < GitProvider
5
+
6
+ private
7
+
8
+ def download
9
+ git_repo.api.download(payload, ref)[:content]
10
+ rescue Octokit::NotFound
11
+ raise Txgh::GitConfigNotFoundError, "Config file #{payload} not found in #{ref}"
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ module Txgh
2
+ module Config
3
+ module Providers
4
+ class GitlabProvider < GitProvider
5
+
6
+ private
7
+
8
+ def download
9
+ gitlab_repo.api.download(payload, ref)[:content]
10
+ rescue ::Gitlab::Error::NotFound
11
+ raise Txgh::GitConfigNotFoundError, "Config file #{payload} not found in #{ref}"
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -1,9 +1,11 @@
1
1
  module Txgh
2
2
  module Config
3
3
  module Providers
4
- autoload :FileProvider, 'txgh/config/providers/file_provider'
5
- autoload :GitProvider, 'txgh/config/providers/git_provider'
6
- autoload :RawProvider, 'txgh/config/providers/raw_provider'
4
+ autoload :FileProvider, 'txgh/config/providers/file_provider'
5
+ autoload :GitProvider, 'txgh/config/providers/git_provider'
6
+ autoload :GithubProvider, 'txgh/config/providers/github_provider'
7
+ autoload :GitlabProvider, 'txgh/config/providers/gitlab_provider'
8
+ autoload :RawProvider, 'txgh/config/providers/raw_provider'
7
9
  end
8
10
  end
9
11
  end
@@ -4,8 +4,8 @@ module Txgh
4
4
  class << self
5
5
  include ProviderSupport
6
6
 
7
- def tx_config(transifex_project, github_repo, ref = nil)
8
- options = { github_repo: github_repo, ref: ref }
7
+ def tx_config(transifex_project, git_repo, ref = nil)
8
+ options = { git_repo: git_repo, ref: ref }
9
9
  scheme, payload = split_uri(transifex_project.tx_config_uri)
10
10
  provider_for(scheme).load(payload, options)
11
11
  end
@@ -0,0 +1,85 @@
1
+ require 'base64'
2
+
3
+ module Txgh
4
+ class GitApi
5
+ class << self
6
+ def create_from_client(client, repo_name)
7
+ new(client, repo_name)
8
+ end
9
+ end
10
+
11
+ attr_reader :client, :repo_name
12
+
13
+ def initialize(client, repo_name)
14
+ @client = client
15
+ @repo_name = repo_name
16
+ end
17
+
18
+ def tree(sha)
19
+ client.tree(repo_name, sha, recursive: 1)
20
+ end
21
+
22
+ def blob(sha)
23
+ client.blob(repo_name, sha)
24
+ end
25
+
26
+ def create_ref(branch, sha)
27
+ client.create_ref(repo_name, branch, sha) rescue false
28
+ end
29
+
30
+ def update_contents(branch, content_list, message)
31
+ content_list.each do |file_params|
32
+ path = file_params.fetch(:path)
33
+ new_contents = file_params.fetch(:contents)
34
+ branch = Utils.relative_branch(branch)
35
+
36
+ file_sha = file_params.fetch(:sha) do
37
+ begin
38
+ client.contents(repo_name, { path: path, ref: branch })[:sha]
39
+ rescue Octokit::NotFound
40
+ nil
41
+ end
42
+ end
43
+
44
+ # If the file doesnt exist, then it isn't tracked by git and file_sha
45
+ # will be nil. In git land, a SHA of all zeroes means create a new file
46
+ # instead of updating an existing one.
47
+ current_sha = file_sha || '0' * 40
48
+ new_sha = Utils.git_hash_blob(new_contents)
49
+ options = { branch: branch }
50
+
51
+ if current_sha != new_sha
52
+ client.update_contents(
53
+ repo_name, path, message, current_sha, new_contents, options
54
+ )
55
+ end
56
+ end
57
+ end
58
+
59
+ def get_commit(sha)
60
+ client.commit(repo_name, sha)
61
+ end
62
+
63
+ def get_ref(ref)
64
+ client.ref(repo_name, ref)
65
+ end
66
+
67
+ def download(path, branch)
68
+ file = client.contents(repo_name, { path: path, ref: branch }).to_h
69
+
70
+ file[:content] = case file[:encoding]
71
+ when 'base64'
72
+ Base64.decode64(file[:content])
73
+ else
74
+ file[:content].force_encoding(file[:encoding])
75
+ end
76
+
77
+ file.delete(:encoding)
78
+ file
79
+ end
80
+
81
+ def create_status(sha, state, options = {})
82
+ client.create_status(repo_name, sha, state, options)
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,90 @@
1
+ module Txgh
2
+ class GitRepo
3
+ attr_reader :config, :api
4
+
5
+ def initialize(config, api)
6
+ @config = config
7
+ @api = api
8
+ end
9
+
10
+ def name
11
+ config['name']
12
+ end
13
+
14
+ def branch
15
+ config['branch']
16
+ end
17
+
18
+ def tag
19
+ config['tag']
20
+ end
21
+
22
+ def process_all_branches?
23
+ branch == 'all'
24
+ end
25
+
26
+ def upload_diffs?
27
+ !(diff_point || '').empty?
28
+ end
29
+
30
+ def diff_point
31
+ config['diff_point']
32
+ end
33
+
34
+ def process_all_tags?
35
+ tag == 'all'
36
+ end
37
+
38
+ def should_process_ref?(candidate)
39
+ if Utils.is_tag?(candidate)
40
+ should_process_tag?(candidate)
41
+ else
42
+ should_process_branch?(candidate)
43
+ end
44
+ end
45
+
46
+ def git_config_branch
47
+ @git_config_branch ||= begin
48
+ if process_all_branches?
49
+ 'all'
50
+ else
51
+ Utils.absolute_branch(branch || 'master')
52
+ end
53
+ end
54
+ end
55
+
56
+ def git_config_tag
57
+ @git_config_tag ||= begin
58
+ if process_all_tags?
59
+ 'all'
60
+ else
61
+ Utils.absolute_branch(tag) if tag
62
+ end
63
+ end
64
+ end
65
+
66
+ def webhook_secret
67
+ config['webhook_secret']
68
+ end
69
+
70
+ def webhook_protected?
71
+ !(webhook_secret || '').empty?
72
+ end
73
+
74
+ def commit_message
75
+ config['commit_message']
76
+ end
77
+
78
+ private
79
+
80
+ def should_process_branch?(candidate)
81
+ process_all_branches? || candidate.include?(git_config_branch)
82
+ end
83
+
84
+ def should_process_tag?(candidate)
85
+ process_all_tags? || (
86
+ git_config_tag && candidate.include?(git_config_tag)
87
+ )
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,151 @@
1
+ require 'celluloid/current'
2
+
3
+ module Txgh
4
+ class GitStatus
5
+ class State
6
+ PENDING = 'pending'
7
+ SUCCESS = 'success'
8
+ ERROR = 'error'
9
+ FAILURE = 'failure'
10
+
11
+ class << self
12
+ def pending; PENDING; end
13
+ def success; SUCCESS; end
14
+ def error; ERROR; end
15
+ def failure; failure; end
16
+ end
17
+ end
18
+
19
+ ALL_COMPLETE_DESCRIPTION = "Translations complete!"
20
+ TARGET_URL_TEMPLATE = "https://www.transifex.com/%{organization}/%{project_slug}/content"
21
+ DESCRIPTION_TEMPLATE = "%{complete}/%{total} translations complete."
22
+ CONTEXT = 'continuous-localization/txgh'
23
+
24
+ class << self
25
+ def update(project, repo, branch)
26
+ new(project, repo, branch).update
27
+ end
28
+
29
+ def error(project, repo, branch, options = {})
30
+ new(project, repo, branch).error(options)
31
+ end
32
+ end
33
+
34
+ attr_reader :project, :repo, :branch
35
+
36
+ def initialize(project, repo, branch)
37
+ @project = project
38
+ @repo = repo
39
+ @branch = branch
40
+ end
41
+
42
+ def update
43
+ return if tx_resources.empty?
44
+
45
+ repo.api.create_status(
46
+ sha, state, {
47
+ context: context, target_url: target_url, description: description
48
+ }
49
+ )
50
+ end
51
+
52
+ def error(options = {})
53
+ repo.api.create_status(
54
+ sha, State.error, {
55
+ context: context,
56
+ target_url: options.fetch(:target_url),
57
+ description: options.fetch(:description)
58
+ }
59
+ )
60
+ end
61
+
62
+ private
63
+
64
+ def sha
65
+ ref[:object][:sha]
66
+ end
67
+
68
+ def ref
69
+ @ref ||= repo.api.get_ref(branch)
70
+ end
71
+
72
+ def context
73
+ CONTEXT
74
+ end
75
+
76
+ def target_url
77
+ # assume all resources are from the same project
78
+ TARGET_URL_TEMPLATE % {
79
+ organization: project.organization,
80
+ project_slug: tx_resources.first.project_slug
81
+ }
82
+ end
83
+
84
+ def state
85
+ if all_complete?
86
+ State.success
87
+ else
88
+ State.pending
89
+ end
90
+ end
91
+
92
+ def description
93
+ if all_complete?
94
+ ALL_COMPLETE_DESCRIPTION
95
+ else
96
+ DESCRIPTION_TEMPLATE % stat_totals
97
+ end
98
+ end
99
+
100
+ def all_complete?
101
+ stats.all? do |resource_stats|
102
+ resource_stats.all? do |locale, details|
103
+ details['completed'] == '100%'
104
+ end
105
+ end
106
+ end
107
+
108
+ def stat_totals
109
+ @stat_totals ||= { complete: 0, total: 0 }.tap do |counts|
110
+ stats.each do |resource_stats|
111
+ resource_stats.each_pair do |locale, details|
112
+ counts[:total] += details['translated_entities'] + details['untranslated_entities']
113
+ counts[:complete] += details['translated_entities']
114
+ end
115
+ end
116
+ end
117
+ end
118
+
119
+ def stats
120
+ @stats ||= tx_resources.map do |tx_resource|
121
+ Celluloid::Future.new { project.api.get_stats(*tx_resource.slugs) }
122
+ end.map(&:value)
123
+ end
124
+
125
+ def tx_resources
126
+ @tx_resources ||=
127
+ tx_config.resources.each_with_object([]) do |tx_resource, ret|
128
+ if repo.process_all_branches?
129
+ tx_resource = Txgh::TxBranchResource.new(tx_resource, branch)
130
+ end
131
+
132
+ next unless existing_slugs.include?(tx_resource.resource_slug)
133
+ ret << tx_resource
134
+ end
135
+ end
136
+
137
+ def existing_slugs
138
+ @existing_slugs ||= existing_resources.map do |resource|
139
+ resource['slug']
140
+ end
141
+ end
142
+
143
+ def existing_resources
144
+ @existing_resources ||= project.api.get_resources(project.name)
145
+ end
146
+
147
+ def tx_config
148
+ @tx_config ||= Txgh::Config::TxManager.tx_config(project, repo, branch)
149
+ end
150
+ end
151
+ end
@@ -1,25 +1,13 @@
1
- require 'base64'
2
1
  require 'octokit'
3
2
 
4
3
  module Txgh
5
- class GithubApi
4
+ class GithubApi < GitApi
6
5
  class << self
7
6
  def create_from_credentials(login, access_token, repo_name)
8
7
  create_from_client(
9
8
  Octokit::Client.new(login: login, access_token: access_token), repo_name
10
9
  )
11
10
  end
12
-
13
- def create_from_client(client, repo_name)
14
- new(client, repo_name)
15
- end
16
- end
17
-
18
- attr_reader :client, :repo_name
19
-
20
- def initialize(client, repo_name)
21
- @client = client
22
- @repo_name = repo_name
23
11
  end
24
12
 
25
13
  def tree(sha)
@@ -1,92 +1,4 @@
1
1
  module Txgh
2
- class GithubRepo
3
- attr_reader :config, :api
4
-
5
- def initialize(config, api)
6
- @config = config
7
- @api = api
8
- end
9
-
10
- def name
11
- config['name']
12
- end
13
-
14
- def branch
15
- config['branch']
16
- end
17
-
18
- def tag
19
- config['tag']
20
- end
21
-
22
- def process_all_branches?
23
- branch == 'all'
24
- end
25
-
26
- def upload_diffs?
27
- !(diff_point || '').empty?
28
- end
29
-
30
- def diff_point
31
- config['diff_point']
32
- end
33
-
34
- def process_all_tags?
35
- tag == 'all'
36
- end
37
-
38
- def should_process_ref?(candidate)
39
- if Utils.is_tag?(candidate)
40
- should_process_tag?(candidate)
41
- else
42
- should_process_branch?(candidate)
43
- end
44
- end
45
-
46
- def github_config_branch
47
- @github_config_branch ||= begin
48
- if process_all_branches?
49
- 'all'
50
- else
51
- Utils.absolute_branch(branch || 'master')
52
- end
53
- end
54
- end
55
-
56
- def github_config_tag
57
- @github_config_tag ||= begin
58
- if process_all_tags?
59
- 'all'
60
- else
61
- Utils.absolute_branch(tag) if tag
62
- end
63
- end
64
- end
65
-
66
- def webhook_secret
67
- config['webhook_secret']
68
- end
69
-
70
- def webhook_protected?
71
- !(webhook_secret || '').empty?
72
- end
73
-
74
- def commit_message
75
- config['commit_message']
76
- end
77
-
78
- private
79
-
80
- def should_process_branch?(candidate)
81
- process_all_branches? ||
82
- candidate.include?(github_config_branch) ||
83
- candidate.include?('L10N')
84
- end
85
-
86
- def should_process_tag?(candidate)
87
- process_all_tags? || (
88
- github_config_tag && candidate.include?(github_config_tag)
89
- )
90
- end
2
+ class GithubRepo < GitRepo
91
3
  end
92
4
  end