txgh 2.0.1 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 41d11b7b8102cca5e57f005f206a07ae1e80b37b
4
- data.tar.gz: 00d3f323be5b4e668d4ad92e89d4211027899346
3
+ metadata.gz: 602ce638efdadb764883e26d1c3636e84bdd5ede
4
+ data.tar.gz: 11f75ef7992aad2dc8e08a5c63e49f5faf0b140d
5
5
  SHA512:
6
- metadata.gz: 7a609ee7470a0787d7c350ab62fd871eabd615fcf9645d5f9807de6644587da41e52596dcb28437bb568f8a0cc937598db56b9f1e9ccd5358d5a682e02d8cf55
7
- data.tar.gz: 99d8736582500541ba8967d5d621d55a49eec61dd3c0138a0c54f886963112cd52aff95348ce6faa880728e47b7ac4be0cd767ef57b7711c66427dad7dcbfb94
6
+ metadata.gz: 4ffa50d6b1b08c2b754983f4b899ae9f4e25ae224c239efffda00ea3115a94b8873bd1c8a4583cc360036fcc548a7bbd7850d74df3f3299ab17a45325e7f24e3
7
+ data.tar.gz: 58f00d04c536bb5f66a7cb4762bee31bbbf1a59a226f24d62ddc4deb9c53baf0648a89925b995a44308ce68d525f8c03f5b13d700c2d2273f91321933efc97a8
@@ -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
@@ -79,5 +79,9 @@ module Txgh
79
79
  end
80
80
  end
81
81
 
82
+ def create_status(repo, sha, state, options = {})
83
+ client.create_status(repo, sha, state, options)
84
+ end
85
+
82
86
  end
83
87
  end
@@ -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
 
@@ -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: tx_resource.source_file,
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
@@ -1,3 +1,3 @@
1
1
  module Txgh
2
- VERSION = '2.0.1'
2
+ VERSION = '2.1.0'
3
3
  end
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
@@ -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
 
@@ -0,0 +1,12 @@
1
+ class TestEvents < Txgh::Events
2
+ attr_reader :published
3
+
4
+ def initialize
5
+ @published = []
6
+ super
7
+ end
8
+
9
+ def publish(channel, options = {})
10
+ published << { channel: channel, options: options }
11
+ end
12
+ end