txgh 2.0.1 → 2.1.0
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.
- checksums.yaml +4 -4
- data/lib/txgh/events.rb +35 -0
- data/lib/txgh/github_api.rb +4 -0
- data/lib/txgh/github_status.rb +87 -0
- data/lib/txgh/resource_committer.rb +13 -0
- data/lib/txgh/resource_updater.rb +10 -0
- data/lib/txgh/transifex_api.rb +7 -1
- data/lib/txgh/version.rb +1 -1
- data/lib/txgh.rb +22 -0
- data/spec/events_spec.rb +45 -0
- data/spec/github_status_spec.rb +68 -0
- data/spec/handlers/transifex/hook_handler_spec.rb +4 -0
- data/spec/helpers/standard_txgh_setup.rb +3 -1
- data/spec/helpers/test_events.rb +12 -0
- data/spec/integration/cassettes/transifex_hook_endpoint.yml +210 -137
- data/spec/resource_committer_spec.rb +34 -8
- data/spec/resource_updater_spec.rb +17 -0
- data/spec/spec_helper.rb +6 -0
- data/spec/transifex_api_spec.rb +16 -1
- metadata +7 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 602ce638efdadb764883e26d1c3636e84bdd5ede
|
|
4
|
+
data.tar.gz: 11f75ef7992aad2dc8e08a5c63e49f5faf0b140d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4ffa50d6b1b08c2b754983f4b899ae9f4e25ae224c239efffda00ea3115a94b8873bd1c8a4583cc360036fcc548a7bbd7850d74df3f3299ab17a45325e7f24e3
|
|
7
|
+
data.tar.gz: 58f00d04c536bb5f66a7cb4762bee31bbbf1a59a226f24d62ddc4deb9c53baf0648a89925b995a44308ce68d525f8c03f5b13d700c2d2273f91321933efc97a8
|
data/lib/txgh/events.rb
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
module Txgh
|
|
2
|
+
class Events
|
|
3
|
+
ERROR_CHANNEL = 'errors'
|
|
4
|
+
|
|
5
|
+
attr_reader :channel_hash
|
|
6
|
+
|
|
7
|
+
def initialize
|
|
8
|
+
@channel_hash = Hash.new { |h, k| h[k] = [] }
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def subscribe(channel, &block)
|
|
12
|
+
channel_hash[channel] << block
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def publish(channel, options = {})
|
|
16
|
+
channel_hash.fetch(channel, []).each do |callback|
|
|
17
|
+
callback.call(options)
|
|
18
|
+
end
|
|
19
|
+
rescue => e
|
|
20
|
+
publish_error(e)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def channels
|
|
24
|
+
channel_hash.keys
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def publish_error(e)
|
|
30
|
+
# if nobody has subscribed to error events, raise original error
|
|
31
|
+
callbacks = channel_hash.fetch(ERROR_CHANNEL) { raise e }
|
|
32
|
+
callbacks.each { |callback| callback.call(e) }
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
data/lib/txgh/github_api.rb
CHANGED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
module Txgh
|
|
2
|
+
class GithubStatus
|
|
3
|
+
class State
|
|
4
|
+
PENDING = 'pending'
|
|
5
|
+
SUCCESS = 'success'
|
|
6
|
+
ERROR = 'error'
|
|
7
|
+
FAILURE = 'failure'
|
|
8
|
+
|
|
9
|
+
class << self
|
|
10
|
+
def pending; PENDING; end
|
|
11
|
+
def success; SUCCESS; end
|
|
12
|
+
def error; ERROR; end
|
|
13
|
+
def failure; failure; end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
ALL_COMPLETE_DESCRIPTION = "Translations complete!"
|
|
18
|
+
TARGET_URL_TEMPLATE = "https://www.transifex.com/%{organization}/%{project_slug}/%{resource_slug}/"
|
|
19
|
+
DESCRIPTION_TEMPLATE = "%{complete}/%{total} translations complete."
|
|
20
|
+
CONTEXT = 'continuous-localization/txgh'
|
|
21
|
+
|
|
22
|
+
attr_reader :project, :repo, :tx_resource
|
|
23
|
+
|
|
24
|
+
def initialize(project, repo, tx_resource)
|
|
25
|
+
@project = project
|
|
26
|
+
@repo = repo
|
|
27
|
+
@tx_resource = tx_resource
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def update(sha)
|
|
31
|
+
repo.api.create_status(
|
|
32
|
+
repo.name, sha, state, {
|
|
33
|
+
context: context, target_url: target_url, description: description
|
|
34
|
+
}
|
|
35
|
+
)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
def context
|
|
41
|
+
CONTEXT
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def target_url
|
|
45
|
+
TARGET_URL_TEMPLATE % {
|
|
46
|
+
organization: project.organization,
|
|
47
|
+
project_slug: tx_resource.project_slug,
|
|
48
|
+
resource_slug: tx_resource.resource_slug
|
|
49
|
+
}
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def state
|
|
53
|
+
if all_complete?
|
|
54
|
+
State.success
|
|
55
|
+
else
|
|
56
|
+
State.pending
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def description
|
|
61
|
+
if all_complete?
|
|
62
|
+
ALL_COMPLETE_DESCRIPTION
|
|
63
|
+
else
|
|
64
|
+
DESCRIPTION_TEMPLATE % stat_totals
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def all_complete?
|
|
69
|
+
stats.all? do |locale, details|
|
|
70
|
+
details['completed'] == '100%'
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def stat_totals
|
|
75
|
+
@stat_totals ||= { complete: 0, total: 0 }.tap do |counts|
|
|
76
|
+
stats.each_pair do |locale, details|
|
|
77
|
+
counts[:total] += details['translated_entities'] + details['untranslated_entities']
|
|
78
|
+
counts[:complete] += details['translated_entities']
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def stats
|
|
84
|
+
@stats ||= project.api.get_stats(*tx_resource.slugs)
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
@@ -16,12 +16,25 @@ module Txgh
|
|
|
16
16
|
|
|
17
17
|
if translations
|
|
18
18
|
repo.api.commit(repo.name, branch, { file_name => translations })
|
|
19
|
+
fire_event_for(tx_resource, branch, language)
|
|
19
20
|
end
|
|
20
21
|
end
|
|
21
22
|
end
|
|
22
23
|
|
|
23
24
|
private
|
|
24
25
|
|
|
26
|
+
def fire_event_for(tx_resource, branch, language)
|
|
27
|
+
head = repo.api.get_ref(repo.name, branch)
|
|
28
|
+
sha = head[:object][:sha]
|
|
29
|
+
|
|
30
|
+
Txgh.events.publish(
|
|
31
|
+
'github.resource.committed', {
|
|
32
|
+
project: project, repo: repo, resource: tx_resource, sha: sha,
|
|
33
|
+
language: language
|
|
34
|
+
}
|
|
35
|
+
)
|
|
36
|
+
end
|
|
37
|
+
|
|
25
38
|
def download(tx_resource, branch, language)
|
|
26
39
|
downloader = ResourceDownloader.new(
|
|
27
40
|
project, repo, branch, {
|
|
@@ -31,12 +31,22 @@ module Txgh
|
|
|
31
31
|
else
|
|
32
32
|
upload_whole(tx_resource, file, categories)
|
|
33
33
|
end
|
|
34
|
+
|
|
35
|
+
fire_event_for(tx_resource, commit_sha)
|
|
34
36
|
end
|
|
35
37
|
end
|
|
36
38
|
end
|
|
37
39
|
|
|
38
40
|
private
|
|
39
41
|
|
|
42
|
+
def fire_event_for(tx_resource, commit_sha)
|
|
43
|
+
Txgh.events.publish(
|
|
44
|
+
'transifex.resource.updated', {
|
|
45
|
+
project: project, repo: repo, resource: tx_resource, sha: commit_sha
|
|
46
|
+
}
|
|
47
|
+
)
|
|
48
|
+
end
|
|
49
|
+
|
|
40
50
|
def upload_whole(tx_resource, file, categories)
|
|
41
51
|
content = contents_of(file['sha'])
|
|
42
52
|
|
data/lib/txgh/transifex_api.rb
CHANGED
|
@@ -50,9 +50,15 @@ module Txgh
|
|
|
50
50
|
end
|
|
51
51
|
|
|
52
52
|
def create(tx_resource, content, categories = [])
|
|
53
|
+
name = if tx_resource.branch
|
|
54
|
+
"#{tx_resource.source_file} (#{tx_resource.branch})"
|
|
55
|
+
else
|
|
56
|
+
tx_resource.source_file
|
|
57
|
+
end
|
|
58
|
+
|
|
53
59
|
payload = {
|
|
54
60
|
slug: tx_resource.resource_slug,
|
|
55
|
-
name:
|
|
61
|
+
name: name,
|
|
56
62
|
i18n_type: tx_resource.type,
|
|
57
63
|
categories: CategorySupport.join_categories(categories.uniq),
|
|
58
64
|
content: get_content_io(tx_resource, content)
|
data/lib/txgh/version.rb
CHANGED
data/lib/txgh.rb
CHANGED
|
@@ -7,9 +7,11 @@ module Txgh
|
|
|
7
7
|
autoload :Config, 'txgh/config'
|
|
8
8
|
autoload :DiffCalculator, 'txgh/diff_calculator'
|
|
9
9
|
autoload :EmptyResourceContents, 'txgh/empty_resource_contents'
|
|
10
|
+
autoload :Events, 'txgh/events'
|
|
10
11
|
autoload :GithubApi, 'txgh/github_api'
|
|
11
12
|
autoload :GithubRepo, 'txgh/github_repo'
|
|
12
13
|
autoload :GithubRequestAuth, 'txgh/github_request_auth'
|
|
14
|
+
autoload :GithubStatus, 'txgh/github_status'
|
|
13
15
|
autoload :Handlers, 'txgh/handlers'
|
|
14
16
|
autoload :Hooks, 'txgh/app'
|
|
15
17
|
autoload :MergeCalculator, 'txgh/merge_calculator'
|
|
@@ -40,6 +42,18 @@ module Txgh
|
|
|
40
42
|
def providers
|
|
41
43
|
Txgh::Config::Providers
|
|
42
44
|
end
|
|
45
|
+
|
|
46
|
+
def events
|
|
47
|
+
@events ||= Events.new
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def update_status_callback(options)
|
|
51
|
+
project = options.fetch(:project)
|
|
52
|
+
repo = options.fetch(:repo)
|
|
53
|
+
resource = options.fetch(:resource)
|
|
54
|
+
|
|
55
|
+
GithubStatus.new(project, repo, resource).update(options.fetch(:sha))
|
|
56
|
+
end
|
|
43
57
|
end
|
|
44
58
|
|
|
45
59
|
# default set of tx config providers
|
|
@@ -50,4 +64,12 @@ module Txgh
|
|
|
50
64
|
# default set of base config providers
|
|
51
65
|
key_manager.register_provider(providers::FileProvider, YAML)
|
|
52
66
|
key_manager.register_provider(providers::RawProvider, YAML)
|
|
67
|
+
|
|
68
|
+
events.subscribe('transifex.resource.updated') do |options|
|
|
69
|
+
update_status_callback(options)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
events.subscribe('github.resource.committed') do |options|
|
|
73
|
+
update_status_callback(options)
|
|
74
|
+
end
|
|
53
75
|
end
|
data/spec/events_spec.rb
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
include Txgh
|
|
4
|
+
|
|
5
|
+
describe Events do
|
|
6
|
+
let(:events) { Events.new }
|
|
7
|
+
|
|
8
|
+
describe '#subscribe and #channels' do
|
|
9
|
+
it "adds a channel if it doesn't already exist" do
|
|
10
|
+
expect(events.channels).to be_empty
|
|
11
|
+
events.subscribe('foo.bar')
|
|
12
|
+
expect(events.channels).to eq(['foo.bar'])
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it 'adds the proc to the list of callbacks for the channel' do
|
|
16
|
+
events.subscribe('foo.bar') { 'baz' }
|
|
17
|
+
expect(events.channel_hash['foo.bar'].first.call).to eq('baz')
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
describe '#publish' do
|
|
22
|
+
it 'notifies all subscribers' do
|
|
23
|
+
received = []
|
|
24
|
+
events.subscribe('foo.bar') { |arg| received << "foo.bar #{arg}" }
|
|
25
|
+
events.subscribe('foo.bar') { |arg| received << "foo.bar2 #{arg}" }
|
|
26
|
+
events.publish('foo.bar', 'baz')
|
|
27
|
+
expect(received).to eq([
|
|
28
|
+
'foo.bar baz', 'foo.bar2 baz'
|
|
29
|
+
])
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it 'publishes errors through a special errors channel' do
|
|
33
|
+
errors = []
|
|
34
|
+
events.subscribe('errors') { |e| errors << e }
|
|
35
|
+
events.subscribe('foo.bar') { raise 'jelly beans' }
|
|
36
|
+
expect { events.publish('foo.bar') }.to_not raise_error
|
|
37
|
+
expect(errors.first.message).to eq('jelly beans')
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it 'raises errors if there are no error subscribers' do
|
|
41
|
+
events.subscribe('foo.bar') { raise 'jelly beans' }
|
|
42
|
+
expect { events.publish('foo.bar') }.to raise_error('jelly beans')
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'helpers/standard_txgh_setup'
|
|
3
|
+
|
|
4
|
+
include Txgh
|
|
5
|
+
|
|
6
|
+
describe GithubStatus do
|
|
7
|
+
include StandardTxghSetup
|
|
8
|
+
|
|
9
|
+
describe '#update' do
|
|
10
|
+
let(:status) { GithubStatus.new(transifex_project, github_repo, resource) }
|
|
11
|
+
let(:resource) { tx_config.resource(resource_slug) }
|
|
12
|
+
let(:sha) { 'abc123shashasha' }
|
|
13
|
+
|
|
14
|
+
let(:stats) do
|
|
15
|
+
supported_languages.each_with_object({}) do |language, ret|
|
|
16
|
+
ret[language] = {
|
|
17
|
+
'translated_entities' => 10, 'untranslated_entities' => 0,
|
|
18
|
+
'completed' => '100%'
|
|
19
|
+
}
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
before(:each) do
|
|
24
|
+
allow(transifex_api).to receive(:get_stats).and_return(stats)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
context 'with all resources at 100%' do
|
|
28
|
+
it 'reports status as success' do
|
|
29
|
+
expect(github_api).to receive(:create_status) do |repo, commit_sha, state, options|
|
|
30
|
+
expect(repo).to eq(repo_name)
|
|
31
|
+
expect(commit_sha).to eq(sha)
|
|
32
|
+
expect(state).to eq(GithubStatus::State.success)
|
|
33
|
+
expect(options[:description]).to eq('Translations complete!')
|
|
34
|
+
expect(options[:context]).to eq('continuous-localization/txgh')
|
|
35
|
+
expect(options[:target_url]).to eq(
|
|
36
|
+
"https://www.transifex.com/#{organization}/#{project_name}/#{resource_slug}/"
|
|
37
|
+
)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
status.update(sha)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
context 'with one language at less than 100%' do
|
|
45
|
+
let(:stats) do
|
|
46
|
+
{
|
|
47
|
+
'pt' => {
|
|
48
|
+
'translated_entities' => 10, 'untranslated_entities' => 0,
|
|
49
|
+
'completed' => '100%'
|
|
50
|
+
},
|
|
51
|
+
'ja' => {
|
|
52
|
+
'translated_entities' => 5, 'untranslated_entities' => 5,
|
|
53
|
+
'completed' => '50%'
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
it 'reports status as pending' do
|
|
59
|
+
expect(github_api).to receive(:create_status) do |repo, commit_sha, state, options|
|
|
60
|
+
expect(state).to eq(GithubStatus::State.pending)
|
|
61
|
+
expect(options[:description]).to eq('15/20 translations complete.')
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
status.update(sha)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -31,6 +31,10 @@ describe HookHandler do
|
|
|
31
31
|
allow(downloader).to(receive(:first)).and_return([
|
|
32
32
|
"translations/#{language}/sample.yml", translations
|
|
33
33
|
])
|
|
34
|
+
|
|
35
|
+
allow(github_api).to receive(:get_ref).and_return(
|
|
36
|
+
object: { sha: '123abcshashasha' }
|
|
37
|
+
)
|
|
34
38
|
end
|
|
35
39
|
|
|
36
40
|
it 'downloads translations and pushes them to the correct branch (head)' do
|
|
@@ -18,6 +18,7 @@ module StandardTxghSetup
|
|
|
18
18
|
let(:supported_languages) { [language] }
|
|
19
19
|
let(:translations) { 'translation file contents' }
|
|
20
20
|
let(:diff_point) { nil }
|
|
21
|
+
let(:organization) { 'myorg' }
|
|
21
22
|
|
|
22
23
|
let(:project_config) do
|
|
23
24
|
{
|
|
@@ -28,7 +29,8 @@ module StandardTxghSetup
|
|
|
28
29
|
'tx_config' => "raw://#{tx_config_raw}",
|
|
29
30
|
'webhook_secret' => 'abc123',
|
|
30
31
|
'auto_delete_resources' => 'true',
|
|
31
|
-
'languages' => supported_languages
|
|
32
|
+
'languages' => supported_languages,
|
|
33
|
+
'organization' => organization
|
|
32
34
|
}
|
|
33
35
|
end
|
|
34
36
|
|