phraseapp_updater 2.0.4 → 2.0.5

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 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