-
Notifications
You must be signed in to change notification settings - Fork 9
Expand file tree
/
Copy pathaction.yml
More file actions
436 lines (398 loc) · 17.5 KB
/
action.yml
File metadata and controls
436 lines (398 loc) · 17.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
name: 'Pull from Lokalise'
description: GitHub action to download translation files from Lokalise TMS to your GitHub repository in the form of a pull request.
author: 'Lokalise Group, Ilya Krukowski'
inputs:
api_token:
description: 'API token for Lokalise with read/write permissions'
required: true
project_id:
description: 'Project ID for Lokalise'
required: true
base_lang:
description: 'Base language (e.g., en, fr_FR)'
required: true
default: 'en'
translations_path:
description: 'Paths to translation files'
required: true
default: |
locales
file_format:
description: 'Format of the translation files (e.g., json). Find all supported file formats at https://developers.lokalise.com/reference/api-file-formats'
required: true
default: 'json'
file_ext:
description: 'Custom file extension(s) to use when searching for translation files. You can pass either a single string (e.g. json) or multiple extensions as newline-separated values (YAML block). By default, the extension is inferred from the file_format value. However, for certain formats (e.g., json_structured) or bundles (e.g., iOS with .strings and .stringsdict), this parameter allows specifying one or more extensions manually to ensure proper file matching.'
required: false
default: ''
additional_params:
description: 'Additional parameters for Lokalise API on pull. Must be a valid JSON or YAML. Find all supported options at https://developers.lokalise.com/reference/download-files'
required: false
default: ''
temp_branch_prefix:
description: 'Prefix for the temp branch to create pull request'
required: false
default: 'lok'
always_pull_base:
description: 'By default, changes in the base language translation files are ignored. Set this to true to include base language translations in the PR.'
required: false
default: 'false'
flat_naming:
description: 'Use flat naming convention (true/false). If true, expects files like locales/en.json instead of locales/en/file.json'
required: false
default: 'false'
skip_include_tags:
description: 'Skip setting the include-tags argument during download. This will download all translation keys for the specified format, regardless of tags.'
required: false
default: 'false'
skip_original_filenames:
description: "Skips setting the --original-filenames and --directory-prefix arguments during download. By default, the action enables --original-filenames=true and sets a directory-prefix to /. When --original-filenames is set to false, all translation keys are exported into a single file per language, and --directory-prefix has no effect."
required: false
default: 'false'
pr_labels:
description: 'Comma-separated list of labels to apply to the created pull request'
required: false
default: ''
pr_title:
description: 'Title of the pull request. If not provided, defaults to "Lokalise translations update".'
required: false
default: 'Lokalise translations update'
pr_body:
description: 'Body of the pull request. If not provided, defaults to "This PR updates translations from Lokalise."'
required: false
default: 'This PR updates translations from Lokalise.'
pr_reviewers:
description: 'Comma-separated list of GitHub usernames to assign as reviewers for the pull request.'
required: false
default: ''
pr_teams_reviewers:
description: 'Comma-separated list of team slugs within the organization to assign as reviewers for the pull request.'
required: false
default: ''
pr_draft:
description: 'Create PR as draft'
required: false
default: 'false'
pr_assignees:
description: 'Comma-separated list of assignees for the PR'
required: false
default: ''
max_retries:
description: 'Maximum number of retries on rate limit and other retryable errors'
required: false
default: '3'
sleep_on_retry:
description: 'Number of seconds to sleep before retrying'
required: false
default: '1'
http_timeout:
description: 'Timeout for HTTP calls (in seconds)'
required: false
default: '120'
download_timeout:
description: 'Timeout for the whole download operation (in seconds)'
required: false
default: '600'
async_mode:
description: 'Use async mode for translations download'
required: false
default: 'false'
async_poll_initial_wait:
description: 'Number of seconds to wait before polling the async download process for the first time'
required: false
default: '1'
async_poll_max_wait:
description: 'Timeout for polling the async download process'
required: false
default: '120'
os_platform:
description: 'Target platform for the binary (linux_amd64, linux_arm64, mac_amd64, mac_arm64). If not set, the action will auto-detect based on the runner.'
required: false
default: ''
git_user_name:
description: 'Git username to set in git config. If not provided, defaults to GitHub actor.'
required: false
default: ''
git_user_email:
description: 'Git email to set in git config. If not provided, uses "<username>@users.noreply.github.com".'
required: false
default: ''
git_commit_message:
description: 'Git commit message used. If not provided, defaults to "Translations update".'
required: false
default: 'Translations update'
git_sign_commits:
description: 'Use `git commit -S` (requires signing to be configured in the workflow)'
required: false
default: 'false'
custom_github_token:
description: 'Optional GitHub token to use for API operations instead of the default GITHUB_TOKEN. Useful for custom permissions or elevated scopes.'
required: false
default: ''
override_branch_name:
description: 'Optional static branch name to use instead of auto-generating one. Useful when you want to reuse the same PR.'
required: false
default: ''
override_base_branch:
description: 'Override base branch to use for the Lokalise PR (defaults to triggering branch)'
required: false
default: ''
force_push:
description: 'Force push changes to the remote branch (overwrites history). Use with caution. Defaults to false.'
required: false
default: 'false'
post_process_command:
description: 'Shell command to run after downloading translation files and before committing. Useful for custom replacements, cleanup, or any post-processing logic. Still experimental.'
required: false
default: ''
post_process_strict:
description: 'Whether to fail the action if the post_process_command returns a non-zero exit code'
required: false
default: 'false'
branding:
icon: 'download-cloud'
color: 'orange'
outputs:
created_branch:
description: "Branch used for the PR (new temp branch on manual runs, PR head on PR runs)"
value: ${{ steps.create-commit.outputs.branch_name }}
pr_exists:
description: "True if a PR exists after this run (created or updated)"
value: ${{ steps.create-update-pr.outputs.pr_exists }}
pr_created:
description: "True if a brand-new PR was created by this run"
value: ${{ steps.create-update-pr.outputs.pr_created }}
pr_updated:
description: "True if an existing PR was updated by this run"
value: ${{ steps.create-update-pr.outputs.pr_updated }}
pr_action:
description: "Either 'created', 'updated', or 'none'"
value: ${{ steps.create-update-pr.outputs.pr_action }}
pr_number:
description: "Pull request number (created or existing)"
value: ${{ steps.create-update-pr.outputs.pr_number }}
pr_id:
description: "Pull request ID (created or existing)"
value: ${{ steps.create-update-pr.outputs.pr_id }}
pr_url:
description: "Pull request URL (created or existing)"
value: ${{ steps.create-update-pr.outputs.pr_url }}
runs:
using: "composite"
steps:
- name: Detect platform
id: detect-platform
shell: bash
env:
OS_PLATFORM: "${{ inputs.os_platform }}"
run: |
set -euo pipefail
# Normalize user-provided os_platform: trim, drop spaces, lowercase
OS_PLATFORM="$(printf '%s' "${OS_PLATFORM:-}" \
| sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' -e 's/[[:space:]]//g' \
| tr '[:upper:]' '[:lower:]')"
validate_platform() {
case "$1" in
linux_amd64|linux_arm64|mac_amd64|mac_arm64) return 0 ;;
*) return 1 ;;
esac
}
if [[ -n "$OS_PLATFORM" ]]; then
if ! validate_platform "$OS_PLATFORM"; then
echo "Error: unsupported 'os_platform' input: '$OS_PLATFORM'"
echo "Supported values: linux_amd64, linux_arm64, mac_amd64, mac_arm64"
exit 1
fi
echo "Using user-provided platform: $OS_PLATFORM"
PLATFORM="$OS_PLATFORM"
else
# Auto-detect from runner, but normalize to lowercase and tolerate x64/amd64 variants
runner_os="$(printf '%s' "${RUNNER_OS:-}" | tr '[:upper:]' '[:lower:]')"
runner_arch="$(printf '%s' "${RUNNER_ARCH:-}" | tr '[:upper:]' '[:lower:]')"
echo "Auto-detecting platform from runner: OS=${runner_os} ARCH=${runner_arch}"
case "${runner_os}/${runner_arch}" in
linux/x64|linux/amd64) PLATFORM="linux_amd64" ;;
linux/arm64) PLATFORM="linux_arm64" ;;
macos/x64|macos/amd64) PLATFORM="mac_amd64" ;;
macos/arm64) PLATFORM="mac_arm64" ;;
*)
echo "Error: unsupported runner platform: ${RUNNER_OS:-}/${RUNNER_ARCH:-}"
echo "Please set 'os_platform' input explicitly to one of: linux_amd64, linux_arm64, mac_amd64, mac_arm64"
exit 1
;;
esac
fi
echo "Detected platform: $PLATFORM"
echo "platform=$PLATFORM" >> "$GITHUB_OUTPUT"
- name: Pull translation files from Lokalise
id: pull-files
shell: bash
env:
LOKALISE_API_KEY: "${{ inputs.api_token }}"
LOKALISE_PROJECT_ID: "${{ inputs.project_id }}"
FILE_EXT: "${{ inputs.file_ext }}"
ADDITIONAL_PARAMS: "${{ inputs.additional_params }}"
MAX_RETRIES: "${{ inputs.max_retries }}"
SLEEP_TIME: "${{ inputs.sleep_on_retry }}"
FILE_FORMAT: "${{ inputs.file_format }}"
TRANSLATIONS_PATH: "${{ inputs.translations_path }}"
BASE_LANG: "${{ inputs.base_lang }}"
ALWAYS_PULL_BASE: "${{ inputs.always_pull_base }}"
FLAT_NAMING: "${{ inputs.flat_naming }}"
HTTP_TIMEOUT: "${{ inputs.http_timeout }}"
DOWNLOAD_TIMEOUT: "${{ inputs.download_timeout }}"
SKIP_INCLUDE_TAGS: "${{ inputs.skip_include_tags }}"
SKIP_ORIGINAL_FILENAMES: "${{ inputs.skip_original_filenames }}"
PLATFORM: "${{ steps.detect-platform.outputs.platform }}"
ASYNC_MODE: "${{ inputs.async_mode }}"
ASYNC_POLL_INITIAL_WAIT: "${{ inputs.async_poll_initial_wait }}"
ASYNC_POLL_MAX_WAIT: "${{ inputs.async_poll_max_wait }}"
run: |
set -euo pipefail
echo "Downloading translation files from Lokalise..."
CMD_PATH="${{ github.action_path }}/bin/lokalise_download_${PLATFORM}"
if [ ! -f "$CMD_PATH" ]; then
echo "Error: Binary for platform '${PLATFORM}' not found!"
exit 1
fi
chmod +x "$CMD_PATH"
"$CMD_PATH" || {
echo "Error: lokalise_download script failed with exit code $?"
exit 1
}
echo "Download complete! Detecting changed files..."
CMD_PATH="${{ github.action_path }}/bin/detect_changed_files_${PLATFORM}"
if [ ! -f "$CMD_PATH" ]; then
echo "Error: Binary for platform '${PLATFORM}' not found!"
exit 1
fi
chmod +x "$CMD_PATH"
"$CMD_PATH" || {
echo "Error: detect_changed_files script failed with exit code $?"
exit 1
}
- name: Run post-processing command
if: inputs.post_process_command != ''
env:
FILE_FORMAT: "${{ inputs.file_format }}"
FILE_EXT: "${{ inputs.file_ext }}"
TRANSLATIONS_PATH: "${{ inputs.translations_path }}"
BASE_LANG: "${{ inputs.base_lang }}"
FLAT_NAMING: "${{ inputs.flat_naming }}"
PLATFORM: "${{ steps.detect-platform.outputs.platform }}"
STRICT: "${{ inputs.post_process_strict }}"
POST_CMD: "${{ inputs.post_process_command }}"
shell: bash
run: |
echo "Running post-process command:"
printf '%s\n' "$POST_CMD"
set +e
bash -eo pipefail -lc "$POST_CMD"
EXIT_CODE=$?
set -e
if [[ "$STRICT" == "true" && "$EXIT_CODE" -ne 0 ]]; then
echo "Post-processing failed with exit code $EXIT_CODE and strict mode is enabled. Exiting."
exit "$EXIT_CODE"
elif [[ "$EXIT_CODE" -ne 0 ]]; then
echo "Post-processing failed with exit code $EXIT_CODE, but strict mode is off. Continuing..."
else
echo "Post-processing finished successfully."
fi
- name: Commit changes
id: create-commit
if: steps.pull-files.outputs.has_changes == 'true'
env:
BASE_REF: "${{ inputs.override_base_branch || github.event.pull_request.base.ref || github.ref_name }}"
HEAD_REF: "${{ github.event.pull_request.head.ref || '' }}"
FILE_FORMAT: "${{ inputs.file_format }}"
FILE_EXT: "${{ inputs.file_ext }}"
TRANSLATIONS_PATH: "${{ inputs.translations_path }}"
BASE_LANG: "${{ inputs.base_lang }}"
ALWAYS_PULL_BASE: "${{ inputs.always_pull_base }}"
FLAT_NAMING: "${{ inputs.flat_naming }}"
TEMP_BRANCH_PREFIX: "${{ inputs.temp_branch_prefix }}"
PLATFORM: "${{ steps.detect-platform.outputs.platform }}"
GIT_USER_NAME: "${{ inputs.git_user_name }}"
GIT_USER_EMAIL: "${{ inputs.git_user_email }}"
GIT_COMMIT_MESSAGE: "${{ inputs.git_commit_message }}"
GIT_SIGN_COMMITS: "${{ inputs.git_sign_commits }}"
OVERRIDE_BRANCH_NAME: "${{ github.event.pull_request.head.ref || inputs.override_branch_name }}"
FORCE_PUSH: "${{ inputs.force_push }}"
shell: bash
run: |
set -euo pipefail
echo "Committing changes..."
CMD_PATH="${{ github.action_path }}/bin/commit_changes_${PLATFORM}"
if [ ! -f "$CMD_PATH" ]; then
echo "Error: Binary for platform '${PLATFORM}' not found!"
exit 1
fi
chmod +x "$CMD_PATH"
"$CMD_PATH" || {
echo "Error: commit_changes script failed with exit code $?"
echo "has_changes=false" >> $GITHUB_OUTPUT
exit 1
}
echo "Commit changes script has been executed."
- name: Create or Update Pull Request
if: steps.pull-files.outputs.has_changes == 'true' && steps.create-commit.outputs.commit_created == 'true'
env:
BRANCH_NAME: "${{ steps.create-commit.outputs.branch_name }}"
BASE_REF: "${{ inputs.override_base_branch || github.event.pull_request.base.ref || github.ref_name }}"
PR_LABELS: "${{ inputs.pr_labels }}"
PR_TITLE: "${{ inputs.pr_title }}"
PR_BODY: "${{ inputs.pr_body }}"
PR_TEAMS_REVIEWERS: "${{ inputs.pr_teams_reviewers }}"
PR_REVIEWERS: "${{ inputs.pr_reviewers }}"
PR_DRAFT: "${{ inputs.pr_draft }}"
PR_ASSIGNEES: "${{ inputs.pr_assignees }}"
id: create-update-pr
# actions/github-script@v8.0.0
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd
with:
github-token: ${{ inputs.custom_github_token || github.token }}
result-encoding: string
script: |
const path = require('path');
const createOrUpdatePR = require(path.join(process.env.GITHUB_ACTION_PATH, 'js/create-update-pr.js'));
const result = await createOrUpdatePR({ github, context });
const created = !!result.created;
const updated = !created && !!result.pr;
const exists = !!result.pr;
core.setOutput('pr_created', created);
core.setOutput('pr_updated', updated);
core.setOutput('pr_exists', exists);
core.setOutput('pr_number', result.pr?.number || '');
core.setOutput('pr_id', result.pr?.id || '');
core.setOutput('pr_url', result.pr?.html_url || '');
const action = created ? 'created' : (updated ? 'updated' : 'none');
core.setOutput('pr_action', action);
return action;
- name: Verify PR presence
id: check-pr-created
shell: bash
run: |
set -e
if [ "${{ steps.pull-files.outputs.has_changes }}" != "true" ]; then
echo "No changes detected, nothing to commit."
exit 0
fi
if [ "${{ steps.create-commit.outputs.commit_created }}" != "true" ]; then
echo "Changes detected but no commit was created (e.g., filtered/excluded)."
exit 0
fi
echo "PR action: ${{ steps.create-update-pr.outputs.pr_action }}"
echo "PR number: ${{ steps.create-update-pr.outputs.pr_number }}"
echo "PR url: ${{ steps.create-update-pr.outputs.pr_url }}"
if [ "${{ steps.create-update-pr.outputs.pr_exists }}" = "true" ]; then
if [ "${{ steps.create-update-pr.outputs.pr_created }}" = "true" ]; then
echo "PR was created."
elif [ "${{ steps.create-update-pr.outputs.pr_updated }}" = "true" ]; then
echo "PR was updated."
else
echo "PR exists, but neither created nor updated flags are true (noop?)."
fi
else
echo "Commit created but no PR exists (unexpected)."
exit 1
fi