phraseapp_updater 2.1.1 → 3.0.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/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.
|