phraseapp_updater 2.0.4 → 2.0.5

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
  SHA256:
3
- metadata.gz: 90f3eb1a3fc564c81e37c06c3de35f659cb27490e3b7280f329e762a505e78c4
4
- data.tar.gz: 8548103775510c36d7bad59957de3397cd1b3550fb2de8f4d37ff609a7ce120c
3
+ metadata.gz: 6279bf1f85138efd48f74dfad8c33bc374c484d763a13864a985b6b7008cb877
4
+ data.tar.gz: 4d18b2a5b27fed7564625b9ee19b4172a1bfabf976cb889d8f468ac57e3c9a22
5
5
  SHA512:
6
- metadata.gz: 0f4afe7e685edf935a7f68f8d2682b88c660c01a79ea644fa5205e25450af8108bee671334415408b416e9a1d9a4c4eb01b9ddb4da2e635b22c953c5cc83e0ff
7
- data.tar.gz: db6342214253090ae81c35fa058719a9c96863bf17d11f47577a6312542c64e5782fb59b4fb71416f2ab88a7bcbc03526f182c330adee1f6d865db144fab70d8
6
+ metadata.gz: 8d31f320c4c72c182f3664f90a9d19fe07a8b5debc4d4253f092c5ab16effa25aa94ac2281a6a3a857da500042f354ad20c5852a943fd311faf5dbbd79bd8f38
7
+ data.tar.gz: 1ab2a71f775c3eb7ed9c0d8c2d2ca9ee92b5d633de1439e929a096fa9f1b3b7ee66da9e6756ae058804936f37ce1a5c075306853bfd68c2a541cca760bb7a4f5
@@ -7,6 +7,7 @@ require 'phraseapp_updater'
7
7
  class PhraseAppUpdaterCLI < Thor
8
8
  class_option :default_locale, type: :string, default: 'en', desc: 'PhraseApp default locale'
9
9
  class_option :file_format, type: :string, default: 'json', desc: 'Filetype of localization files.'
10
+ class_option :verbose, type: :boolean, default: false, desc: 'Verbose output'
10
11
 
11
12
  desc 'setup <locale_path>',
12
13
  'Create a new PhraseApp project, initializing it with locale files at <locale_path>. the new project ID is printed to STDOUT'
@@ -22,7 +23,8 @@ class PhraseAppUpdaterCLI < Thor
22
23
  options[:phraseapp_api_key],
23
24
  options[:phraseapp_project_name],
24
25
  options[:file_format],
25
- options[:parent_commit])
26
+ options[:parent_commit],
27
+ verbose: options[:verbose])
26
28
 
27
29
  updater.upload_directory(locales_path)
28
30
 
@@ -50,6 +52,7 @@ class PhraseAppUpdaterCLI < Thor
50
52
  ENV['PREFIX'] = options[:prefix]
51
53
  ENV['BRANCH'] = options.fetch(:branch) { sh('git rev-parse --abbrev-ref HEAD').chomp }
52
54
  ENV['REMOTE'] = options.fetch(:remote) { sh("git config branch.#{ENV['BRANCH']}.remote").chomp }
55
+ ENV['VERBOSE'] = options[:verbose] ? 't' : 'f'
53
56
 
54
57
  shell_script_path = File.join(__dir__, 'synchronize_phraseapp.sh')
55
58
  exec(shell_script_path)
@@ -67,7 +70,8 @@ class PhraseAppUpdaterCLI < Thor
67
70
  updater = PhraseAppUpdater.new(
68
71
  options[:phraseapp_api_key],
69
72
  options[:phraseapp_project_id],
70
- options[:file_format])
73
+ options[:file_format],
74
+ verbose: options[:verbose])
71
75
 
72
76
  updater.download_to_directory(target_path)
73
77
  parent_commit = updater.read_parent_commit
@@ -94,7 +98,8 @@ class PhraseAppUpdaterCLI < Thor
94
98
  updater = PhraseAppUpdater.new(
95
99
  options[:phraseapp_api_key],
96
100
  options[:phraseapp_project_id],
97
- options[:file_format])
101
+ options[:file_format],
102
+ verbose: options[:verbose])
98
103
 
99
104
  updater.upload_directory(source_path)
100
105
  updater.update_parent_commit(options[:parent_commit])
@@ -110,7 +115,8 @@ class PhraseAppUpdaterCLI < Thor
110
115
  updater = PhraseAppUpdater.new(
111
116
  options[:phraseapp_api_key],
112
117
  options[:phraseapp_project_id],
113
- options[:file_format])
118
+ options[:file_format],
119
+ verbose: options[:verbose])
114
120
 
115
121
  updater.update_parent_commit(options[:parent_commit])
116
122
  end
@@ -131,7 +137,7 @@ class PhraseAppUpdaterCLI < Thor
131
137
  validate_readable_path!('path2', path2)
132
138
 
133
139
  handle_errors do
134
- updater = PhraseAppUpdater.new(nil, nil, options[:file_format])
140
+ updater = PhraseAppUpdater.new(nil, nil, options[:file_format], verbose: options[:verbose])
135
141
  diffs = updater.diff_directories(path1, path2)
136
142
  if diffs.empty?
137
143
  exit(0)
@@ -162,7 +168,7 @@ class PhraseAppUpdaterCLI < Thor
162
168
  validate_writable_path!('to', result_path)
163
169
 
164
170
  handle_errors do
165
- updater = PhraseAppUpdater.new(nil, nil, options[:file_format])
171
+ updater = PhraseAppUpdater.new(nil, nil, options[:file_format], verbose: options[:verbose])
166
172
  updater.merge_directories(our_path, their_path, ancestor_path, result_path)
167
173
  end
168
174
  end
@@ -192,7 +198,7 @@ class PhraseAppUpdaterCLI < Thor
192
198
  # pass `nil` to `merge_files`.
193
199
  ancestor = nil if File.zero?(ancestor)
194
200
 
195
- updater = PhraseAppUpdater.new(nil, nil, file_format)
201
+ updater = PhraseAppUpdater.new(nil, nil, file_format, verbose: options[:verbose])
196
202
  updater.merge_files(ours, theirs, ancestor, to)
197
203
  end
198
204
 
@@ -13,7 +13,7 @@ fi
13
13
 
14
14
  # Configuration is via environment, and expected to be provided from a Ruby
15
15
  # driver.
16
- for variable in PHRASEAPP_API_KEY PHRASEAPP_PROJECT_ID BRANCH REMOTE PREFIX FILE_FORMAT NO_COMMIT; do
16
+ for variable in PHRASEAPP_API_KEY PHRASEAPP_PROJECT_ID BRANCH REMOTE PREFIX FILE_FORMAT NO_COMMIT VERBOSE; do
17
17
  if [ -z "${!variable}" ]; then
18
18
  echo "Error: must specify $variable" >&2
19
19
  exit 1
@@ -30,6 +30,7 @@ current_phraseapp_path=$(make_temporary_directory)
30
30
  common_ancestor=$(phraseapp_updater download "${current_phraseapp_path}" \
31
31
  --phraseapp_api_key="${PHRASEAPP_API_KEY}" \
32
32
  --phraseapp_project_id="${PHRASEAPP_PROJECT_ID}" \
33
+ --verbose="${VERBOSE}" \
33
34
  --file_format="${FILE_FORMAT}")
34
35
 
35
36
  # If common_ancestor is not available or reachable from BRANCH, we've been
@@ -74,6 +75,7 @@ if [ "${phraseapp_changed}" = 't' ] && [ "${branch_changed}" = 't' ]; then
74
75
  merge_resolution_path=$(make_temporary_directory)
75
76
  phraseapp_updater merge "${common_ancestor_path}" "${current_branch_path}" "${current_phraseapp_path}" \
76
77
  --to "${merge_resolution_path}" \
78
+ --verbose="${VERBOSE}" \
77
79
  --file_format="${FILE_FORMAT}"
78
80
 
79
81
  if [ "$NO_COMMIT" != 't' ]; then
@@ -92,6 +94,7 @@ if [ "${phraseapp_changed}" = 't' ] && [ "${branch_changed}" = 't' ]; then
92
94
  -p "${current_branch}" \
93
95
  -p "${phraseapp_commit}" \
94
96
  -m "Merged locale changes from PhraseApp" \
97
+ -m "Since common ancestor ${common_ancestor}" \
95
98
  -m "X-PhraseApp-Merge: ${phraseapp_commit}")
96
99
 
97
100
  # Push to BRANCH
@@ -108,6 +111,7 @@ if [ "${phraseapp_changed}" = 't' ] && [ "${branch_changed}" = 't' ]; then
108
111
  --parent_commit="${new_parent_commit}" \
109
112
  --phraseapp_api_key="${PHRASEAPP_API_KEY}" \
110
113
  --phraseapp_project_id="${PHRASEAPP_PROJECT_ID}" \
114
+ --verbose="${VERBOSE}" \
111
115
  --file_format="${FILE_FORMAT}"
112
116
 
113
117
  elif [ "${branch_changed}" = 't' ]; then
@@ -118,6 +122,7 @@ elif [ "${branch_changed}" = 't' ]; then
118
122
  --parent_commit="${current_branch}" \
119
123
  --phraseapp_api_key="${PHRASEAPP_API_KEY}" \
120
124
  --phraseapp_project_id="${PHRASEAPP_PROJECT_ID}" \
125
+ --verbose="${VERBOSE}" \
121
126
  --file_format="${FILE_FORMAT}"
122
127
 
123
128
  elif [ "${phraseapp_changed}" = 't' ]; then
@@ -127,7 +132,8 @@ elif [ "${phraseapp_changed}" = 't' ]; then
127
132
  updated_branch_tree=$(replace_nested_tree "${current_branch}^{tree}" "${PREFIX}" "${current_phraseapp_tree}")
128
133
  update_commit=$(git commit-tree "${updated_branch_tree}" \
129
134
  -p "${current_branch}" \
130
- -m "Incorporate locale changes from PhraseApp")
135
+ -m "Incorporate locale changes from PhraseApp" \
136
+ -m "Since common ancestor ${common_ancestor}")
131
137
 
132
138
  git push "${REMOTE}" "${update_commit}:refs/heads/${BRANCH}"
133
139
 
@@ -135,6 +141,7 @@ elif [ "${phraseapp_changed}" = 't' ]; then
135
141
  phraseapp_updater update_parent_commit \
136
142
  --parent_commit="${update_commit}" \
137
143
  --phraseapp_api_key="${PHRASEAPP_API_KEY}" \
144
+ --verbose="${VERBOSE}" \
138
145
  --phraseapp_project_id="${PHRASEAPP_PROJECT_ID}"
139
146
  else
140
147
  echo "Only PhraseApp changed: not committing to $BRANCH branch" >&2
@@ -10,16 +10,17 @@ require 'phraseapp_updater/yml_config_loader'
10
10
  class PhraseAppUpdater
11
11
  using IndexBy
12
12
 
13
- def self.for_new_project(phraseapp_api_key, phraseapp_project_name, file_format, parent_commit)
13
+ def self.for_new_project(phraseapp_api_key, phraseapp_project_name, file_format, parent_commit, verbose: false)
14
14
  api = PhraseAppAPI.new(phraseapp_api_key, nil, LocaleFile.class_for_file_format(file_format))
15
15
  project_id = api.create_project(phraseapp_project_name, parent_commit)
16
- return self.new(phraseapp_api_key, project_id, file_format), project_id
16
+ return self.new(phraseapp_api_key, project_id, file_format, verbose: verbose), project_id
17
17
  end
18
18
 
19
- def initialize(phraseapp_api_key, phraseapp_project_id, file_format, default_locale: 'en')
19
+ def initialize(phraseapp_api_key, phraseapp_project_id, file_format, default_locale: 'en', verbose: false)
20
20
  @locale_file_class = LocaleFile.class_for_file_format(file_format)
21
21
  @default_locale = default_locale
22
22
  @phraseapp_api = PhraseAppAPI.new(phraseapp_api_key, phraseapp_project_id, @locale_file_class)
23
+ @verbose = verbose
23
24
  end
24
25
 
25
26
  def diff_directories(our_path, their_path)
@@ -84,6 +85,7 @@ class PhraseAppUpdater
84
85
  locale_names = Set.new.merge(ours.keys).merge(theirs.keys)
85
86
 
86
87
  locale_names.map do |locale_name|
88
+ STDERR.puts "Merging #{locale_name}" if @verbose
87
89
  our_file = ours[locale_name]
88
90
  their_file = theirs[locale_name]
89
91
  ancestor_file = ancestors[locale_name]
@@ -118,7 +120,7 @@ class PhraseAppUpdater
118
120
  else
119
121
  ancestor_file ||= empty_locale_file(our_file.locale_name)
120
122
 
121
- resolved_content = Differ.resolve!(
123
+ resolved_content = Differ.new(verbose: @verbose).resolve!(
122
124
  original: ancestor_file.parsed_content,
123
125
  primary: our_file.parsed_content,
124
126
  secondary: their_file.parsed_content)
@@ -1,144 +1,160 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'set'
2
4
  require 'hashdiff'
3
5
  require 'deep_merge'
4
6
 
5
7
  class PhraseAppUpdater
6
8
  class Differ
7
- SEPARATOR = "~~~"
9
+ SEPARATOR = '~~~'
8
10
  using IndexBy
9
11
 
10
- class << self
11
- # Resolution strategy is that primary always wins in the event of a conflict
12
- def resolve_diffs(primary:, secondary:, secondary_deleted_prefixes:)
13
- primary = primary.index_by { |op, path, from, to| path }
14
- secondary = secondary.index_by { |op, path, from, to| path }
15
-
16
- # As well as explicit conflicts, we want to make sure that deletions or
17
- # incompatible type changes to a `primary` key prevent addition of child
18
- # keys in `secondary`. Because input hashes are flattened, it's never
19
- # possible for a given path and its prefix to be in the same input.
20
- # For example, in:
21
- #
22
- # primary = [["+", "a", 1]]
23
- # secondary = [["+", "a.b", 2]]
24
- #
25
- # the secondary change is impossible to perform on top of the primary, and
26
- # must be blocked.
27
- #
28
- # This applies in reverse: prefixes of paths in `p` need to be available
29
- # as hashes, so must not appear as terminals in `s`:
30
- #
31
- # primary = [["+", "a.b", 2]]
32
- # secondary = [["+", "a", 1]]
33
- primary_prefixes = primary.keys.flat_map { |p| path_prefixes(p) }.to_set
34
-
35
- # Remove conflicting entries from secondary, recording incompatible
36
- # changes.
37
- path_conflicts = []
38
- secondary.delete_if do |path, diff|
39
- if primary_prefixes.include?(path) || primary.keys.any? { |pk| path.start_with?(pk) }
40
- path_conflicts << path unless primary.has_key?(path) && diff == primary[path]
41
- true
42
- else
43
- false
44
- end
45
- end
46
-
47
- # For all path conflicts matching secondary_deleted_prefixes, additionally
48
- # remove other changes with the same prefix.
49
- prefix_conflicts = secondary_deleted_prefixes.select do |prefix|
50
- path_conflicts.any? { |path| path.start_with?(prefix) }
51
- end
12
+ def initialize(verbose: false)
13
+ @verbose = verbose
14
+ end
52
15
 
53
- secondary.delete_if do |path, diff|
54
- prefix_conflicts.any? { |prefix| path.start_with?(prefix) }
16
+ # Resolution strategy is that primary always wins in the event of a conflict
17
+ def resolve_diffs(primary:, secondary:, secondary_deleted_prefixes:)
18
+ primary = primary.index_by { |op, path, from, to| path }
19
+ secondary = secondary.index_by { |op, path, from, to| path }
20
+
21
+ # As well as explicit conflicts, we want to make sure that deletions or
22
+ # incompatible type changes to a `primary` key prevent addition of child
23
+ # keys in `secondary`. Because input hashes are flattened, it's never
24
+ # possible for a given path and its prefix to be in the same input.
25
+ # For example, in:
26
+ #
27
+ # primary = [["+", "a", 1]]
28
+ # secondary = [["+", "a.b", 2]]
29
+ #
30
+ # the secondary change is impossible to perform on top of the primary, and
31
+ # must be blocked.
32
+ #
33
+ # This applies in reverse: prefixes of paths in `p` need to be available
34
+ # as hashes, so must not appear as terminals in `s`:
35
+ #
36
+ # primary = [["+", "a.b", 2]]
37
+ # secondary = [["+", "a", 1]]
38
+ primary_prefixes = primary.keys.flat_map { |p| path_prefixes(p) }.to_set
39
+
40
+ # Remove conflicting entries from secondary, recording incompatible
41
+ # changes.
42
+ path_conflicts = []
43
+ secondary.delete_if do |path, diff|
44
+ if primary_prefixes.include?(path) || primary.keys.any? { |pk| path.start_with?(pk) }
45
+ path_conflicts << path unless primary.has_key?(path) && diff == primary[path]
46
+ true
47
+ else
48
+ false
55
49
  end
50
+ end
56
51
 
57
- primary.values + secondary.values
52
+ # For all path conflicts matching secondary_deleted_prefixes, additionally
53
+ # remove other changes with the same prefix.
54
+ prefix_conflicts = secondary_deleted_prefixes.select do |prefix|
55
+ path_conflicts.any? { |path| path.start_with?(prefix) }
58
56
  end
59
57
 
60
- def apply_diffs(hash, diffs)
61
- deep_compact!(HashDiff.patch!(hash, diffs))
58
+ secondary.delete_if do |path, diff|
59
+ prefix_conflicts.any? { |prefix| path.start_with?(prefix) }
62
60
  end
63
61
 
64
- def resolve!(original:, primary:, secondary:)
65
- # To appropriately cope with type changes on either sides, flatten the
66
- # trees before calculating the difference and then expand afterwards.
67
- f_original = flatten(original)
68
- f_primary = flatten(primary)
69
- f_secondary = flatten(secondary)
70
-
71
- primary_diffs = HashDiff.diff(f_original, f_primary)
72
- secondary_diffs = HashDiff.diff(f_original, f_secondary)
73
-
74
- # However, flattening discards one critical piece of information: when we
75
- # have deleted or clobbered an entire prefix (subtree) from the original,
76
- # we want to consider this deletion atomic. If any of the changes is
77
- # cancelled, they must all be. Motivating example:
78
- #
79
- # original: { word: { one: "..", "many": ".." } }
80
- # primary: { word: { one: "..", "many": "..", "zero": ".." } }
81
- # secondary: { word: ".." }
82
- # would unexpectedly result in { word: { zero: ".." } }.
83
- #
84
- # Additionally calculate subtree prefixes that were deleted in `secondary`:
85
- secondary_deleted_prefixes =
86
- HashDiff.diff(original, secondary, delimiter: SEPARATOR).lazy
62
+ primary.values + secondary.values
63
+ end
64
+
65
+ def apply_diffs(hash, diffs)
66
+ deep_compact!(HashDiff.patch!(hash, diffs))
67
+ end
68
+
69
+ def resolve!(original:, primary:, secondary:)
70
+ # To appropriately cope with type changes on either sides, flatten the
71
+ # trees before calculating the difference and then expand afterwards.
72
+ f_original = flatten(original)
73
+ f_primary = flatten(primary)
74
+ f_secondary = flatten(secondary)
75
+
76
+ primary_diffs = HashDiff.diff(f_original, f_primary)
77
+ secondary_diffs = HashDiff.diff(f_original, f_secondary)
78
+
79
+ # However, flattening discards one critical piece of information: when we
80
+ # have deleted or clobbered an entire prefix (subtree) from the original,
81
+ # we want to consider this deletion atomic. If any of the changes is
82
+ # cancelled, they must all be. Motivating example:
83
+ #
84
+ # original: { word: { one: "..", "many": ".." } }
85
+ # primary: { word: { one: "..", "many": "..", "zero": ".." } }
86
+ # secondary: { word: ".." }
87
+ # would unexpectedly result in { word: { zero: ".." } }.
88
+ #
89
+ # Additionally calculate subtree prefixes that were deleted in `secondary`:
90
+ secondary_deleted_prefixes =
91
+ HashDiff.diff(original, secondary, delimiter: SEPARATOR).lazy
87
92
  .select { |op, path, from, to| (op == "-" || op == "~") && from.is_a?(Hash) && !to.is_a?(Hash) }
88
93
  .map { |op, path, from, to| path }
89
94
  .to_a
90
95
 
91
96
 
92
- resolved_diffs = resolve_diffs(primary: primary_diffs,
93
- secondary: secondary_diffs,
94
- secondary_deleted_prefixes: secondary_deleted_prefixes)
95
- HashDiff.patch!(f_original, resolved_diffs)
97
+ resolved_diffs = resolve_diffs(primary: primary_diffs,
98
+ secondary: secondary_diffs,
99
+ secondary_deleted_prefixes: secondary_deleted_prefixes)
96
100
 
97
- expand(f_original)
98
- end
101
+ if @verbose
102
+ STDERR.puts('Primary diffs:')
103
+ primary_diffs.each { |d| STDERR.puts(d.inspect) }
99
104
 
105
+ STDERR.puts('Secondary diffs:')
106
+ secondary_diffs.each { |d| STDERR.puts(d.inspect) }
100
107
 
101
- # Prefer everything in current except deletions,
102
- # which are restored from previous if available
103
- def restore_deletions(current, previous)
104
- current.deep_merge(previous)
108
+ STDERR.puts('Resolution:')
109
+ resolved_diffs.each { |d| STDERR.puts(d.inspect) }
105
110
  end
106
111
 
107
- private
112
+ HashDiff.patch!(f_original, resolved_diffs)
113
+
114
+ expand(f_original)
115
+ end
116
+
117
+
118
+ # Prefer everything in current except deletions,
119
+ # which are restored from previous if available
120
+ def restore_deletions(current, previous)
121
+ current.deep_merge(previous)
122
+ end
123
+
124
+ private
108
125
 
109
- def flatten(hash, prefix = nil, acc = {})
110
- hash.each do |k, v|
111
- k = "#{prefix}#{SEPARATOR}#{k}" if prefix
112
- if v.is_a?(Hash)
113
- flatten(v, k, acc)
114
- else
115
- acc[k] = v
116
- end
126
+ def flatten(hash, prefix = nil, acc = {})
127
+ hash.each do |k, v|
128
+ k = "#{prefix}#{SEPARATOR}#{k}" if prefix
129
+ if v.is_a?(Hash)
130
+ flatten(v, k, acc)
131
+ else
132
+ acc[k] = v
117
133
  end
118
- acc
119
134
  end
135
+ acc
136
+ end
120
137
 
121
- def expand(flat_hash)
122
- flat_hash.each_with_object({}) do |(key, value), root|
123
- path = key.split(SEPARATOR)
124
- leaf_key = path.pop
125
- leaf = path.inject(root) do |node, path_key|
126
- node[path_key] ||= {}
127
- end
128
- raise ArgumentError.new("Type conflict in flattened hash expand: expected no key at #{key}") if leaf.has_key?(leaf_key)
129
- leaf[leaf_key] = value
138
+ def expand(flat_hash)
139
+ flat_hash.each_with_object({}) do |(key, value), root|
140
+ path = key.split(SEPARATOR)
141
+ leaf_key = path.pop
142
+ leaf = path.inject(root) do |node, path_key|
143
+ node[path_key] ||= {}
130
144
  end
145
+ raise ArgumentError.new("Type conflict in flattened hash expand: expected no key at #{key}") if leaf.has_key?(leaf_key)
146
+ leaf[leaf_key] = value
131
147
  end
148
+ end
132
149
 
133
- def path_prefixes(path_string)
134
- path = path_string.split(SEPARATOR)
135
- parents = []
136
- path.inject do |acc, el|
137
- parents << acc
138
- "#{acc}#{SEPARATOR}#{el}"
139
- end
140
- parents
150
+ def path_prefixes(path_string)
151
+ path = path_string.split(SEPARATOR)
152
+ parents = []
153
+ path.inject do |acc, el|
154
+ parents << acc
155
+ "#{acc}#{SEPARATOR}#{el}"
141
156
  end
157
+ parents
142
158
  end
143
159
  end
144
160
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class PhraseAppUpdater
4
- VERSION = '2.0.4'
4
+ VERSION = '2.0.5'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: phraseapp_updater
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.4
4
+ version: 2.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kevin Griffin
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-12-07 00:00:00.000000000 Z
11
+ date: 2019-02-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor