phraseapp_updater 2.1.1 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/phraseapp_common.sh +13 -7
- data/bin/synchronize_phraseapp.sh +4 -4
- data/lib/phraseapp_updater/phraseapp_api.rb +134 -98
- data/lib/phraseapp_updater/version.rb +1 -1
- data/lib/phraseapp_updater.rb +5 -4
- metadata +13 -28
- data/.circleci/config.yml +0 -98
- data/.envrc +0 -1
- data/.gitignore +0 -9
- data/.rspec +0 -2
- data/.travis.yml +0 -5
- data/CODE_OF_CONDUCT.markdown +0 -50
- data/Gemfile +0 -8
- data/README.markdown +0 -177
- data/Rakefile +0 -7
- data/nix/gem/Gemfile +0 -24
- data/nix/gem/Gemfile.lock +0 -68
- data/nix/gem/gemset.nix +0 -230
- data/nix/generate.rb +0 -41
- data/phraseapp_updater.gemspec +0 -35
- data/shell.nix +0 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f33deb3a77a37a47abdf44c354526cbe3ad3d10256bb2a046bf48922178187fd
|
4
|
+
data.tar.gz: f22f2ceed18653b09c24be4724707cdc40c39539535c226fd028bc03338437cd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 822cc67247e2e2752397127f6b4e5e0d893cdfc3c64a29f14067d2bc5b6e131f3924d5cad982960df65da4b47971c12ad43fc9caf9c9ff15e756a596a64c163e
|
7
|
+
data.tar.gz: a2e1152e1a376690722722d73c2654d4ab4e417530b66b86f7562cd0b17e54f06976a295bae601ec0d4ed1e8570a518eef9a37ff317f24001643e711c0365e6c
|
data/bin/phraseapp_common.sh
CHANGED
@@ -1,16 +1,22 @@
|
|
1
1
|
#!/usr/bin/env bash
|
2
2
|
|
3
3
|
# Set up a working directory
|
4
|
-
working_directory=$(mktemp -d
|
4
|
+
working_directory=$(mktemp -d phraseapp.XXXXXX)
|
5
5
|
|
6
6
|
function cleanup_working_directory(){
|
7
|
-
|
7
|
+
local now archive
|
8
|
+
now="$(date "+%Y%m%d%H%M%S")"
|
9
|
+
archive="/tmp/phraseapp-updater-$now.tar.gz"
|
10
|
+
tar -C "$(dirname "$working_directory")" -czf "$archive" "$(basename "${working_directory}")"
|
11
|
+
rm -rf "${working_directory}"
|
12
|
+
echo "Working files saved to $archive"
|
8
13
|
}
|
9
14
|
|
10
15
|
trap "cleanup_working_directory" EXIT SIGINT
|
11
16
|
|
12
17
|
function make_temporary_directory() {
|
13
|
-
|
18
|
+
name="${1:-tmp}"
|
19
|
+
mktemp -d "${working_directory}/$name.XXXXXXXX"
|
14
20
|
}
|
15
21
|
|
16
22
|
function make_tree_from_directory() {
|
@@ -35,15 +41,15 @@ function make_tree_from_directory() {
|
|
35
41
|
}
|
36
42
|
|
37
43
|
|
38
|
-
function
|
39
|
-
extract_files "$1:${PREFIX}"
|
44
|
+
function extract_prefix_from_commit() {
|
45
|
+
extract_files "$1" "$2:${PREFIX}"
|
40
46
|
}
|
41
47
|
|
42
48
|
function extract_files() {
|
43
49
|
local path
|
44
|
-
path=$(make_temporary_directory)
|
50
|
+
path=$(make_temporary_directory "git.${1}.${2%%:*}")
|
45
51
|
|
46
|
-
git archive --format=tar "$
|
52
|
+
git archive --format=tar "$2" | tar -x -C "${path}"
|
47
53
|
|
48
54
|
echo "${path}"
|
49
55
|
}
|
@@ -34,7 +34,7 @@ if [ "$local_branch" ] && [ "$current_branch" != "$local_branch" ]; then
|
|
34
34
|
fi
|
35
35
|
|
36
36
|
# First, fetch the current contents of PhraseApp's staged ("verified") state.
|
37
|
-
current_phraseapp_path=$(make_temporary_directory)
|
37
|
+
current_phraseapp_path=$(make_temporary_directory phraseapp_download)
|
38
38
|
common_ancestor=$(phraseapp_updater download "${current_phraseapp_path}" \
|
39
39
|
--phraseapp_api_key="${PHRASEAPP_API_KEY}" \
|
40
40
|
--phraseapp_project_id="${PHRASEAPP_PROJECT_ID}" \
|
@@ -61,8 +61,8 @@ elif ! git merge-base --is-ancestor "${common_ancestor}" "${current_branch}"; th
|
|
61
61
|
skip_ancestor_merge=t
|
62
62
|
fi
|
63
63
|
|
64
|
-
current_branch_path=$(
|
65
|
-
common_ancestor_path=$(
|
64
|
+
current_branch_path=$(extract_prefix_from_commit current_branch "${current_branch}")
|
65
|
+
common_ancestor_path=$(extract_prefix_from_commit common_ancestor "${common_ancestor}")
|
66
66
|
|
67
67
|
current_phraseapp_tree=$(make_tree_from_directory "${current_phraseapp_path}")
|
68
68
|
|
@@ -87,7 +87,7 @@ if [ "${phraseapp_changed}" = 't' ] && [ "${branch_changed}" = 't' ]; then
|
|
87
87
|
echo "$BRANCH branch and PhraseApp both changed: 3-way merging" >&2
|
88
88
|
|
89
89
|
# 3-way merge
|
90
|
-
merge_resolution_path=$(make_temporary_directory)
|
90
|
+
merge_resolution_path=$(make_temporary_directory merge_resolution)
|
91
91
|
phraseapp_updater merge "${common_ancestor_path}" "${current_branch_path}" "${current_phraseapp_path}" \
|
92
92
|
--to "${merge_resolution_path}" \
|
93
93
|
--verbose="${VERBOSE}" \
|
@@ -2,7 +2,8 @@
|
|
2
2
|
|
3
3
|
require 'phraseapp_updater/locale_file'
|
4
4
|
require 'phraseapp_updater/index_by'
|
5
|
-
require '
|
5
|
+
require 'uri'
|
6
|
+
require 'phrase'
|
6
7
|
require 'parallel'
|
7
8
|
require 'tempfile'
|
8
9
|
|
@@ -13,16 +14,24 @@ class PhraseAppUpdater
|
|
13
14
|
PAGE_SIZE = 100
|
14
15
|
|
15
16
|
def initialize(api_key, project_id, locale_file_class)
|
16
|
-
|
17
|
+
config = Phrase::Configuration.new do |c|
|
18
|
+
c.api_key['Authorization'] = api_key
|
19
|
+
c.api_key_prefix['Authorization'] = 'token'
|
20
|
+
c.debugging = false
|
21
|
+
end
|
22
|
+
|
23
|
+
@client = Phrase::ApiClient.new(config)
|
17
24
|
@project_id = project_id
|
18
25
|
@locale_file_class = locale_file_class
|
19
26
|
end
|
20
27
|
|
21
28
|
def create_project(name, parent_commit)
|
22
|
-
params =
|
29
|
+
params = Phrase::ProjectCreateParameters.new(
|
23
30
|
name: name,
|
24
31
|
main_format: @locale_file_class.phraseapp_type)
|
25
|
-
|
32
|
+
|
33
|
+
project = phraseapp_request(Phrase::ProjectsApi, :project_create, params)
|
34
|
+
|
26
35
|
STDERR.puts "Created project #{name} for #{parent_commit}"
|
27
36
|
|
28
37
|
@project_id = project.id
|
@@ -32,7 +41,8 @@ class PhraseAppUpdater
|
|
32
41
|
end
|
33
42
|
|
34
43
|
def lookup_project_id(name)
|
35
|
-
result, =
|
44
|
+
result, = paginated_request(Phrase::ProjectsApi, :projects_list, { per_page: PAGE_SIZE }, limit: 1) { |p| p.name == name }
|
45
|
+
|
36
46
|
raise ProjectNotFoundError.new(name) if result.nil?
|
37
47
|
|
38
48
|
result.id
|
@@ -41,9 +51,10 @@ class PhraseAppUpdater
|
|
41
51
|
# We mark projects with their parent git commit using a tag with a
|
42
52
|
# well-known prefix. We only allow one tag with this prefix at once.
|
43
53
|
def read_parent_commit
|
44
|
-
git_tag, =
|
54
|
+
git_tag, = paginated_request(Phrase::TagsApi, :tags_list, @project_id, limit: 1) do |t|
|
45
55
|
t.name.start_with?(GIT_TAG_PREFIX)
|
46
56
|
end
|
57
|
+
|
47
58
|
raise MissingGitParentError.new if git_tag.nil?
|
48
59
|
|
49
60
|
git_tag.name.delete_prefix(GIT_TAG_PREFIX)
|
@@ -51,35 +62,28 @@ class PhraseAppUpdater
|
|
51
62
|
|
52
63
|
def update_parent_commit(commit_hash)
|
53
64
|
previous_parent = read_parent_commit
|
54
|
-
phraseapp_request
|
55
|
-
@client.tag_delete(@project_id, GIT_TAG_PREFIX + previous_parent)
|
56
|
-
end
|
65
|
+
phraseapp_request(Phrase::TagsApi, :tag_delete, @project_id, GIT_TAG_PREFIX + previous_parent)
|
57
66
|
store_parent_commit(commit_hash)
|
58
67
|
end
|
59
68
|
|
60
69
|
def fetch_locales
|
61
|
-
|
62
|
-
|
63
|
-
phraseapp_request { @client.locales_list(@project_id, 1, 100) }.map do |pa_locale|
|
64
|
-
Locale.new(pa_locale)
|
65
|
-
end
|
70
|
+
locales = paginated_request(Phrase::LocalesApi, :locales_list, @project_id)
|
71
|
+
locales.map { |pa_locale| Locale.new(pa_locale) }
|
66
72
|
end
|
67
73
|
|
68
74
|
def create_locale(name, default: false)
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
@client.locale_create(@project_id, params)
|
76
|
-
end
|
75
|
+
params = Phrase::LocaleCreateParameters.new(
|
76
|
+
name: name,
|
77
|
+
code: name,
|
78
|
+
default: default,
|
79
|
+
)
|
80
|
+
phraseapp_request(Phrase::LocalesApi, :locale_create, @project_id, params)
|
77
81
|
end
|
78
82
|
|
79
83
|
def download_files(locales, skip_unverified:)
|
80
84
|
results = threaded_request(locales) do |locale|
|
81
85
|
STDERR.puts "Downloading file for #{locale}"
|
82
|
-
|
86
|
+
download_locale(locale, skip_unverified)
|
83
87
|
end
|
84
88
|
|
85
89
|
locales.zip(results).map do |locale, file_contents|
|
@@ -87,18 +91,65 @@ class PhraseAppUpdater
|
|
87
91
|
end
|
88
92
|
end
|
89
93
|
|
94
|
+
# Empirically, PhraseApp fails to parse the uploaded files when uploaded in
|
95
|
+
# parallel. Give it a better chance by uploading them one at a time.
|
90
96
|
def upload_files(locale_files, default_locale:)
|
91
|
-
|
97
|
+
is_default = ->(l) { l.locale_name == default_locale }
|
92
98
|
|
99
|
+
# Ensure the locales all exist
|
100
|
+
STDERR.puts('Creating locales')
|
101
|
+
known_locales = fetch_locales.index_by(&:name)
|
93
102
|
threaded_request(locale_files) do |locale_file|
|
94
103
|
unless known_locales.has_key?(locale_file.locale_name)
|
95
|
-
create_locale(locale_file.locale_name,
|
96
|
-
|
104
|
+
create_locale(locale_file.locale_name, default: is_default.(locale_file))
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# Upload the files in a stable order, ensuring the default locale is first.
|
109
|
+
locale_files.sort! do |a, b|
|
110
|
+
next -1 if is_default.(a)
|
111
|
+
next 1 if is_default.(b)
|
112
|
+
|
113
|
+
a.locale_name <=> b.locale_name
|
114
|
+
end
|
115
|
+
|
116
|
+
uploads = {}
|
117
|
+
|
118
|
+
uploads = locale_files.to_h do |locale_file|
|
119
|
+
STDERR.puts("Uploading #{locale_file}")
|
120
|
+
upload_id = upload_file(locale_file)
|
121
|
+
[upload_id, locale_file]
|
122
|
+
end
|
123
|
+
|
124
|
+
# Validate the uploads, retrying failures as necessary
|
125
|
+
successful_upload_ids = {}
|
126
|
+
|
127
|
+
STDERR.puts('Verifying uploads...')
|
128
|
+
until uploads.empty?
|
129
|
+
threaded_request(uploads.to_a) do |upload_id, locale_file|
|
130
|
+
upload = phraseapp_request(Phrase::UploadsApi, :upload_show, @project_id, upload_id)
|
131
|
+
|
132
|
+
case upload.state
|
133
|
+
when "enqueued", "processing"
|
134
|
+
STDERR.puts("#{locale_file}: still processing")
|
135
|
+
when "success"
|
136
|
+
STDERR.puts("#{locale_file}: success")
|
137
|
+
successful_upload_ids[locale_file.locale_name] = upload_id
|
138
|
+
uploads.delete(upload_id)
|
139
|
+
when "error"
|
140
|
+
STDERR.puts("#{locale_file}: upload failure, retrying")
|
141
|
+
new_upload_id = upload_file(locale_file)
|
142
|
+
uploads.delete(upload_id)
|
143
|
+
uploads[new_upload_id] = locale_file
|
144
|
+
else
|
145
|
+
raise RuntimeError.new("Unknown upload state: #{upload.state}")
|
146
|
+
end
|
97
147
|
end
|
98
148
|
|
99
|
-
|
100
|
-
upload_file(locale_file)
|
149
|
+
sleep(2) unless uploads.empty?
|
101
150
|
end
|
151
|
+
|
152
|
+
successful_upload_ids
|
102
153
|
end
|
103
154
|
|
104
155
|
def remove_keys_not_in_uploads(upload_ids)
|
@@ -108,105 +159,101 @@ class PhraseAppUpdater
|
|
108
159
|
end
|
109
160
|
end
|
110
161
|
|
111
|
-
def
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
162
|
+
def download_locale(locale, skip_unverified)
|
163
|
+
opts = {
|
164
|
+
file_format: @locale_file_class.phraseapp_type,
|
165
|
+
skip_unverified_translations: skip_unverified,
|
166
|
+
}
|
116
167
|
|
117
|
-
|
168
|
+
# Avoid allocating a tempfile (and emitting unnecessary warnings) by using `return_type` of `String`
|
169
|
+
phraseapp_request(Phrase::LocalesApi, :locale_download, @project_id, locale.id, return_type: 'String', **opts)
|
118
170
|
end
|
119
171
|
|
120
172
|
def upload_file(locale_file)
|
121
|
-
upload_params = create_upload_params(locale_file.locale_name)
|
122
|
-
|
123
173
|
# The PhraseApp gem only accepts a filename to upload,
|
124
174
|
# so we need to write the file out and pass it the path
|
125
175
|
Tempfile.create([locale_file.locale_name, ".json"]) do |f|
|
126
176
|
f.write(locale_file.content)
|
127
177
|
f.close
|
128
178
|
|
129
|
-
|
130
|
-
|
179
|
+
opts = {
|
180
|
+
file: f,
|
181
|
+
file_encoding: 'UTF-8',
|
182
|
+
file_format: @locale_file_class.phraseapp_type,
|
183
|
+
locale_id: locale_file.locale_name,
|
184
|
+
skip_unverification: false,
|
185
|
+
update_translations: true,
|
186
|
+
tags: [generate_upload_tag],
|
187
|
+
}
|
188
|
+
|
189
|
+
result = phraseapp_request(Phrase::UploadsApi, :upload_create, @project_id, **opts)
|
190
|
+
|
191
|
+
result.id
|
131
192
|
end
|
132
193
|
end
|
133
194
|
|
134
195
|
def remove_keys_not_in_upload(upload_id)
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
begin
|
139
|
-
phraseapp_request { @client.keys_delete(@project_id, delete_params) }
|
140
|
-
rescue RuntimeError => _e
|
141
|
-
# PhraseApp will accept but mark invalid uploads, however the gem
|
142
|
-
# returns the same response in both cases. If we call this API
|
143
|
-
# with the ID of an upload of a bad file, it will fail.
|
144
|
-
# This usually occurs when sending up an empty file, which is
|
145
|
-
# a case we can ignore. However, it'd be better to have a way
|
146
|
-
# to detect a bad upload and find the cause.
|
147
|
-
end
|
196
|
+
delete_pattern = "unmentioned_in_upload:#{upload_id}"
|
197
|
+
phraseapp_request(Phrase::KeysApi, :keys_delete_collection, @project_id, q: delete_pattern)
|
148
198
|
end
|
149
199
|
|
150
200
|
private
|
151
201
|
|
152
202
|
def store_parent_commit(commit_hash)
|
153
|
-
params =
|
154
|
-
|
155
|
-
|
203
|
+
params = Phrase::TagCreateParameters.new(name: GIT_TAG_PREFIX + commit_hash)
|
204
|
+
phraseapp_request(Phrase::TagsApi,:tag_create, @project_id, params)
|
205
|
+
end
|
206
|
+
|
207
|
+
def wrap_phrase_errors
|
208
|
+
yield
|
209
|
+
rescue Phrase::ApiError => e
|
210
|
+
if e.code == 401
|
211
|
+
raise BadAPIKeyError.new(e)
|
212
|
+
elsif e.message.match?(/not found/)
|
213
|
+
raise BadProjectIDError.new(e, @project_id)
|
214
|
+
elsif e.message.match?(/has already been taken/)
|
215
|
+
raise ProjectNameTakenError.new(e)
|
216
|
+
else
|
217
|
+
raise
|
218
|
+
end
|
156
219
|
end
|
157
220
|
|
158
|
-
def
|
221
|
+
def paginated_request(api_class, method, *params, limit: nil, **opts, &filter)
|
222
|
+
|
223
|
+
api_instance = api_class.new(@client)
|
159
224
|
page = 1
|
160
225
|
results = []
|
161
226
|
|
162
227
|
loop do
|
163
|
-
response =
|
164
|
-
|
228
|
+
response = wrap_phrase_errors do
|
229
|
+
api_instance.public_send(method, *params, opts.merge(page: page, page_size: PAGE_SIZE))
|
165
230
|
end
|
166
231
|
|
167
|
-
break if response.empty?
|
232
|
+
break if response.data.empty?
|
168
233
|
|
169
|
-
matches = response.
|
234
|
+
matches = response.data
|
235
|
+
matches = matches.filter(&filter) if filter
|
170
236
|
matches = matches[0, limit - results.size] if limit
|
171
237
|
|
172
|
-
unless matches.empty?
|
173
|
-
results.concat(matches)
|
238
|
+
results.concat(matches) unless matches.empty?
|
174
239
|
|
175
|
-
|
176
|
-
|
240
|
+
break if results.size == limit
|
241
|
+
break unless response.next_page?
|
177
242
|
|
178
|
-
page
|
243
|
+
page = response.next_page
|
179
244
|
end
|
180
245
|
|
181
246
|
results
|
182
247
|
end
|
183
248
|
|
184
|
-
def phraseapp_request(
|
185
|
-
|
249
|
+
def phraseapp_request(api_class, method, *params, **opts)
|
250
|
+
api_instance = api_class.new(@client)
|
186
251
|
|
187
|
-
|
188
|
-
|
189
|
-
if err.respond_to?(:error)
|
190
|
-
err.error
|
191
|
-
else
|
192
|
-
err.errors.join('|')
|
193
|
-
end
|
194
|
-
|
195
|
-
raise RuntimeError.new(error)
|
252
|
+
response = wrap_phrase_errors do
|
253
|
+
api_instance.public_send(method, *params, opts)
|
196
254
|
end
|
197
255
|
|
198
|
-
|
199
|
-
|
200
|
-
rescue RuntimeError => e
|
201
|
-
if e.message.match?(/\(401\)/)
|
202
|
-
raise BadAPIKeyError.new(e)
|
203
|
-
elsif e.message.match?(/not found/)
|
204
|
-
raise BadProjectIDError.new(e, @project_id)
|
205
|
-
elsif e.message.match?(/has already been taken/)
|
206
|
-
raise ProjectNameTakenError.new(e)
|
207
|
-
else
|
208
|
-
raise
|
209
|
-
end
|
256
|
+
response.data
|
210
257
|
end
|
211
258
|
|
212
259
|
# PhraseApp allows two concurrent connections at a time.
|
@@ -216,17 +263,6 @@ class PhraseAppUpdater
|
|
216
263
|
Parallel.map(worklist, in_threads: THREAD_COUNT, &block)
|
217
264
|
end
|
218
265
|
|
219
|
-
def create_upload_params(locale_name)
|
220
|
-
upload_params = PhraseApp::RequestParams::UploadParams.new
|
221
|
-
upload_params.file_encoding = 'UTF-8'
|
222
|
-
upload_params.file_format = @locale_file_class.phraseapp_type
|
223
|
-
upload_params.locale_id = locale_name
|
224
|
-
upload_params.skip_unverification = false
|
225
|
-
upload_params.update_translations = true
|
226
|
-
upload_params.tags = [generate_upload_tag]
|
227
|
-
upload_params
|
228
|
-
end
|
229
|
-
|
230
266
|
def generate_upload_tag
|
231
267
|
"phraseapp_updater_upload_#{Time.now.strftime('%Y%m%d%H%M%S')}"
|
232
268
|
end
|
data/lib/phraseapp_updater.rb
CHANGED
@@ -102,13 +102,14 @@ class PhraseAppUpdater
|
|
102
102
|
# We assert that the default locale contains all legitimate strings, and so
|
103
103
|
# we clean up orphaned content on PhraseApp post-upload by removing keys not
|
104
104
|
# in the default locale.
|
105
|
-
|
106
|
-
|
105
|
+
unless locale_files.find { |f| f.locale_name == @default_locale }
|
106
|
+
raise RuntimeError.new("Missing default locale")
|
107
|
+
end
|
107
108
|
|
108
109
|
upload_ids = @phraseapp_api.upload_files(locale_files, default_locale: @default_locale)
|
109
|
-
default_upload_id = upload_ids
|
110
|
+
default_upload_id = upload_ids.fetch(@default_locale)
|
110
111
|
|
111
|
-
STDERR.puts "Removing keys not in default locale upload #{default_upload_id}"
|
112
|
+
STDERR.puts "Removing keys not in default locale '#{@default_locale}' upload '#{default_upload_id}'"
|
112
113
|
@phraseapp_api.remove_keys_not_in_upload(default_upload_id)
|
113
114
|
end
|
114
115
|
|
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:
|
4
|
+
version: 3.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
-
-
|
7
|
+
- iKnow Team
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-02-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|
@@ -25,33 +25,33 @@ dependencies:
|
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0.19'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: phrase
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
33
|
+
version: 2.8.3
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version:
|
40
|
+
version: 2.8.3
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: hashdiff
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version:
|
47
|
+
version: 1.0.1
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version:
|
54
|
+
version: 1.0.1
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: multi_json
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -114,14 +114,14 @@ dependencies:
|
|
114
114
|
requirements:
|
115
115
|
- - "~>"
|
116
116
|
- !ruby/object:Gem::Version
|
117
|
-
version: '
|
117
|
+
version: '2.2'
|
118
118
|
type: :development
|
119
119
|
prerelease: false
|
120
120
|
version_requirements: !ruby/object:Gem::Requirement
|
121
121
|
requirements:
|
122
122
|
- - "~>"
|
123
123
|
- !ruby/object:Gem::Version
|
124
|
-
version: '
|
124
|
+
version: '2.2'
|
125
125
|
- !ruby/object:Gem::Dependency
|
126
126
|
name: rake
|
127
127
|
requirement: !ruby/object:Gem::Requirement
|
@@ -167,22 +167,13 @@ dependencies:
|
|
167
167
|
description: A tool for merging data on PhraseApp with local changes (usually two
|
168
168
|
git revisions)
|
169
169
|
email:
|
170
|
-
-
|
170
|
+
- systems@iknow.jp
|
171
171
|
executables:
|
172
172
|
- phraseapp_updater
|
173
173
|
extensions: []
|
174
174
|
extra_rdoc_files: []
|
175
175
|
files:
|
176
|
-
- ".circleci/config.yml"
|
177
|
-
- ".envrc"
|
178
|
-
- ".gitignore"
|
179
|
-
- ".rspec"
|
180
|
-
- ".travis.yml"
|
181
|
-
- CODE_OF_CONDUCT.markdown
|
182
|
-
- Gemfile
|
183
176
|
- LICENSE.txt
|
184
|
-
- README.markdown
|
185
|
-
- Rakefile
|
186
177
|
- bin/console
|
187
178
|
- bin/phraseapp_common.sh
|
188
179
|
- bin/phraseapp_updater
|
@@ -197,13 +188,7 @@ files:
|
|
197
188
|
- lib/phraseapp_updater/phraseapp_api.rb
|
198
189
|
- lib/phraseapp_updater/version.rb
|
199
190
|
- lib/phraseapp_updater/yml_config_loader.rb
|
200
|
-
|
201
|
-
- nix/gem/Gemfile.lock
|
202
|
-
- nix/gem/gemset.nix
|
203
|
-
- nix/generate.rb
|
204
|
-
- phraseapp_updater.gemspec
|
205
|
-
- shell.nix
|
206
|
-
homepage: https://app.engoo.com
|
191
|
+
homepage: https://github.com/iknow/phraseapp_updater
|
207
192
|
licenses:
|
208
193
|
- MIT
|
209
194
|
metadata: {}
|
@@ -222,7 +207,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
222
207
|
- !ruby/object:Gem::Version
|
223
208
|
version: '0'
|
224
209
|
requirements: []
|
225
|
-
rubygems_version: 3.
|
210
|
+
rubygems_version: 3.1.6
|
226
211
|
signing_key:
|
227
212
|
specification_version: 4
|
228
213
|
summary: A three-way differ for PhraseApp projects.
|