From 0da52b6fb02f935b49cc4041574ccf31545201d5 Mon Sep 17 00:00:00 2001 From: Maksim Fedotov Date: Tue, 17 Feb 2026 18:36:58 +0300 Subject: [PATCH 1/9] chore(ci): add multiple language support for translation-changelog Signed-off-by: Maksim Fedotov --- translate-changelog/action.yml | 196 ++++++++++++++++++++++----------- 1 file changed, 130 insertions(+), 66 deletions(-) diff --git a/translate-changelog/action.yml b/translate-changelog/action.yml index 5ec8aae..11eddef 100644 --- a/translate-changelog/action.yml +++ b/translate-changelog/action.yml @@ -1,11 +1,19 @@ name: 'Translate Changelog and Create PR' -description: 'Translates Russian changelog files to English and creates a PR' +description: 'Translates changelog files between languages and creates a PR' inputs: changelog_path: description: 'Path to CHANGELOG directory' required: false default: 'CHANGELOG' + source_lang: + description: 'Source language code (ru or en)' + required: false + default: 'ru' + target_lang: + description: 'Target language code (en or ru)' + required: false + default: 'en' python_version: description: 'Python version to use' required: false @@ -18,10 +26,10 @@ inputs: outputs: version: description: 'Version of the translated changelog' - ru_file: - description: 'Russian changelog file name' - eng_file: - description: 'English changelog file name' + source_file: + description: 'Source changelog file name' + target_file: + description: 'Target changelog file name' has_changes: description: 'Whether CHANGELOG files were changed in the last commit' @@ -32,17 +40,28 @@ runs: id: check_changes shell: bash run: | - # Проверить, есть ли изменения в CHANGELOG/*.ru.yml в последнем коммите - # Используем git show для получения списка измененных файлов в HEAD + # Проверить, есть ли изменения в CHANGELOG файлах в последнем коммите CHANGELOG_PATH="${{ inputs.changelog_path }}" - CHANGED_FILES=$(git show --name-only --pretty=format: HEAD | grep "^${CHANGELOG_PATH}/.*\.ru\.yml$" || true) - + SOURCE_LANG="${{ inputs.source_lang }}" + + # Определить паттерн исходных файлов на основе source_lang + if [ "$SOURCE_LANG" = "en" ]; then + # Для английского ищем файлы без языкового суффикса + FILE_PATTERN="^${CHANGELOG_PATH}/v[0-9]+\.[0-9]+\.[0-9]+\.yml$" + else + # Для других языков ищем файлы с языковым суффиксом + FILE_PATTERN="^${CHANGELOG_PATH}/v[0-9]+\.[0-9]+\.[0-9]+\.${SOURCE_LANG}\.yml$" + fi + + # Получить список измененных файлов в последнем коммите + CHANGED_FILES=$(git show --name-only --pretty=format: HEAD | grep -E "$FILE_PATTERN" || true) + if [ -n "$CHANGED_FILES" ]; then - echo "CHANGELOG .ru.yml files changed in last commit:" + echo "CHANGELOG files changed in last commit (source_lang=$SOURCE_LANG):" echo "$CHANGED_FILES" echo "has_changes=true" >> $GITHUB_OUTPUT else - echo "No CHANGELOG .ru.yml files changed in last commit, skipping" + echo "No CHANGELOG files changed in last commit for source_lang=$SOURCE_LANG, skipping" echo "has_changes=false" >> $GITHUB_OUTPUT fi @@ -58,50 +77,72 @@ runs: run: | pip install pyyaml deep-translator packaging - - name: Find latest Russian changelog + - name: Find latest changelog if: steps.check_changes.outputs.has_changes == 'true' id: find_changelog shell: bash run: | cd "${{ inputs.changelog_path }}" - - # Найти все русские файлы и отсортировать по semver + + SOURCE_LANG="${{ inputs.source_lang }}" + TARGET_LANG="${{ inputs.target_lang }}" + + # Найти все файлы исходного языка и отсортировать по semver python3 << 'EOF' import os import re from packaging import version - - files = [f for f in os.listdir('.') if f.endswith('.ru.yml')] - + + source_lang = os.environ.get('SOURCE_LANG', 'ru') + target_lang = os.environ.get('TARGET_LANG', 'en') + + # Определить паттерн файлов на основе source_lang + if source_lang == 'en': + # Для английского ищем файлы без языкового суффикса + file_pattern = re.compile(r'^v(\d+\.\d+\.\d+)\.yml$') + source_suffix = '' + else: + # Для других языков ищем файлы с языковым суффиксом + file_pattern = re.compile(rf'^v(\d+\.\d+\.\d+)\.{re.escape(source_lang)}\.yml$') + source_suffix = f'.{source_lang}' + + files = [f for f in os.listdir('.') if file_pattern.match(f)] + # Извлечь версии из имен файлов versions = [] for f in files: - match = re.match(r'v(\d+\.\d+\.\d+)\.ru\.yml', f) + match = file_pattern.match(f) if match: versions.append((version.parse(match.group(1)), f)) - + if not versions: - print("No Russian changelog files found") + print(f"No changelog files found for source language: {source_lang}") exit(1) - + # Отсортировать по версии (последняя версия первая) versions.sort(reverse=True) - latest_version, latest_file = versions[0] - - # Проверить, существует ли уже английский файл - eng_file = latest_file.replace('.ru.yml', '.yml') - if os.path.exists(eng_file): - print(f"English file {eng_file} already exists, skipping") - exit(0) - + latest_version, source_file = versions[0] + + # Определить имя целевого файла version_str = f"v{latest_version}" - import os + if target_lang == 'en': + # Для английского целевой файл без языкового суффикса + target_file = f"{version_str}.yml" + else: + # Для других языков целевой файл с языковым суффиксом + target_file = f"{version_str}.{target_lang}.yml" + + # Проверить, существует ли уже целевой файл + if os.path.exists(target_file): + print(f"Target file {target_file} already exists, skipping") + exit(0) + github_output = os.environ.get('GITHUB_OUTPUT', '/dev/stdout') with open(github_output, 'a') as f: f.write(f"version={version_str}\n") - f.write(f"ru_file={latest_file}\n") - f.write(f"eng_file={eng_file}\n") - print(f"Latest version: {version_str}, file: {latest_file}") + f.write(f"source_file={source_file}\n") + f.write(f"target_file={target_file}\n") + print(f"Latest version: {version_str}, source: {source_file}, target: {target_file}") EOF continue-on-error: true @@ -110,44 +151,48 @@ runs: id: translate shell: bash env: - RU_FILE: ${{ steps.find_changelog.outputs.ru_file }} - ENG_FILE: ${{ steps.find_changelog.outputs.eng_file }} + SOURCE_FILE: ${{ steps.find_changelog.outputs.source_file }} + TARGET_FILE: ${{ steps.find_changelog.outputs.target_file }} + SOURCE_LANG: ${{ inputs.source_lang }} + TARGET_LANG: ${{ inputs.target_lang }} run: | cd "${{ inputs.changelog_path }}" - + python3 << 'EOF' from deep_translator import GoogleTranslator import os - - ru_file = os.environ['RU_FILE'] - eng_file = os.environ['ENG_FILE'] - - # Загрузить русский файл построчно для сохранения форматирования - with open(ru_file, 'r', encoding='utf-8') as f: - ru_lines = f.readlines() - + + source_file = os.environ['SOURCE_FILE'] + target_file = os.environ['TARGET_FILE'] + source_lang = os.environ['SOURCE_LANG'] + target_lang = os.environ['TARGET_LANG'] + + # Загрузить исходный файл построчно для сохранения форматирования + with open(source_file, 'r', encoding='utf-8') as f: + source_lines = f.readlines() + # Перевести содержимое построчно - translator = GoogleTranslator(source='ru', target='en') - + translator = GoogleTranslator(source=source_lang, target=target_lang) + # Сохранить с сохранением оригинального форматирования - with open(eng_file, 'w', encoding='utf-8') as f: - for line in ru_lines: + with open(target_file, 'w', encoding='utf-8') as f: + for line in source_lines: # Пропустить пустые строки if not line.strip(): f.write(line) continue - + # Сохранить оригинальные отступы indent = len(line) - len(line.lstrip()) content = line.strip() - + # Перевести содержимое строки translated_content = translator.translate(content) - + # Записать переведенную строку с сохранением отступов f.write(' ' * indent + translated_content + '\n') - - print(f"Translated {ru_file} to {eng_file}") + + print(f"Translated {source_file} ({source_lang}) to {target_file} ({target_lang})") EOF - name: Get current branch name @@ -164,13 +209,15 @@ runs: shell: bash run: | VERSION="${{ steps.find_changelog.outputs.version }}" - + SOURCE_LANG="${{ inputs.source_lang }}" + TARGET_LANG="${{ inputs.target_lang }}" + git config --local user.email "action@github.com" git config --local user.name "GitHub Action" - + git add "${{ inputs.changelog_path }}/" - git commit -m "Translate changelog ${VERSION} to English" - + git commit -m "Translate changelog ${VERSION} from ${SOURCE_LANG} to ${TARGET_LANG}" + # Пушим коммит в текущую ветку git push origin HEAD @@ -183,41 +230,58 @@ runs: BRANCH_NAME="${{ steps.get_branch.outputs.branch }}" BASE_BRANCH="${{ inputs.base_branch }}" VERSION="${{ steps.find_changelog.outputs.version }}" - + SOURCE_LANG="${{ inputs.source_lang }}" + TARGET_LANG="${{ inputs.target_lang }}" + SOURCE_FILE="${{ steps.find_changelog.outputs.source_file }}" + TARGET_FILE="${{ steps.find_changelog.outputs.target_file }}" + # Проверить, существует ли уже PR для этой ветки EXISTING_PR=$(gh pr list --head "$BRANCH_NAME" --base "$BASE_BRANCH" --json number --jq '.[0].number' 2>/dev/null || echo "") - + if [ -n "$EXISTING_PR" ] && [ "$EXISTING_PR" != "null" ]; then echo "PR #$EXISTING_PR already exists for branch $BRANCH_NAME" exit 0 fi - + + # Определить полные названия языков для PR + case "$SOURCE_LANG" in + ru) SOURCE_LANG_NAME="Russian" ;; + en) SOURCE_LANG_NAME="English" ;; + *) SOURCE_LANG_NAME="$SOURCE_LANG" ;; + esac + + case "$TARGET_LANG" in + ru) TARGET_LANG_NAME="Russian" ;; + en) TARGET_LANG_NAME="English" ;; + *) TARGET_LANG_NAME="$TARGET_LANG" ;; + esac + # Создать временный файл для body PR PR_BODY_FILE=$(mktemp) cat > "$PR_BODY_FILE" << EOF ## Changelog $VERSION - This PR contains the English translation of the Russian changelog for version $VERSION. + This PR contains the $TARGET_LANG_NAME translation of the $SOURCE_LANG_NAME changelog for version $VERSION. - **Source file:** \`${{ inputs.changelog_path }}/${{ steps.find_changelog.outputs.ru_file }}\` - **Translated file:** \`${{ inputs.changelog_path }}/${{ steps.find_changelog.outputs.eng_file }}\` + **Source file:** `${{ inputs.changelog_path }}/${SOURCE_FILE}` + **Translated file:** `${{ inputs.changelog_path }}/${TARGET_FILE}`
Changelog content \`\`\`yaml - $(cat "${{ inputs.changelog_path }}/${{ steps.find_changelog.outputs.eng_file }}") + $(cat "${{ inputs.changelog_path }}/${TARGET_FILE}") \`\`\`
EOF - + # Создать PR gh pr create \ --base "$BASE_BRANCH" \ --head "$BRANCH_NAME" \ --title "$VERSION" \ --body-file "$PR_BODY_FILE" - + # Удалить временный файл rm -f "$PR_BODY_FILE" From 7b111a853877b7aae042445c78a9a1ef13bac455 Mon Sep 17 00:00:00 2001 From: Maksim Fedotov Date: Wed, 18 Feb 2026 11:34:30 +0300 Subject: [PATCH 2/9] add file prefix Signed-off-by: Maksim Fedotov --- translate-changelog/action.yml | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/translate-changelog/action.yml b/translate-changelog/action.yml index 11eddef..99a66f5 100644 --- a/translate-changelog/action.yml +++ b/translate-changelog/action.yml @@ -6,6 +6,10 @@ inputs: description: 'Path to CHANGELOG directory' required: false default: 'CHANGELOG' + file_prefix: + description: 'Prefix for changelog files (e.g., "CHANGELOG-v" or "v")' + required: false + default: 'v' source_lang: description: 'Source language code (ru or en)' required: false @@ -42,15 +46,16 @@ runs: run: | # Проверить, есть ли изменения в CHANGELOG файлах в последнем коммите CHANGELOG_PATH="${{ inputs.changelog_path }}" + FILE_PREFIX="${{ inputs.file_prefix }}" SOURCE_LANG="${{ inputs.source_lang }}" # Определить паттерн исходных файлов на основе source_lang if [ "$SOURCE_LANG" = "en" ]; then # Для английского ищем файлы без языкового суффикса - FILE_PATTERN="^${CHANGELOG_PATH}/v[0-9]+\.[0-9]+\.[0-9]+\.yml$" + FILE_PATTERN="^${CHANGELOG_PATH}/${FILE_PREFIX}[0-9]+\.[0-9]+\.[0-9]+\.yml$" else # Для других языков ищем файлы с языковым суффиксом - FILE_PATTERN="^${CHANGELOG_PATH}/v[0-9]+\.[0-9]+\.[0-9]+\.${SOURCE_LANG}\.yml$" + FILE_PATTERN="^${CHANGELOG_PATH}/${FILE_PREFIX}[0-9]+\.[0-9]+\.[0-9]+\.${SOURCE_LANG}\.yml$" fi # Получить список измененных файлов в последнем коммите @@ -84,6 +89,7 @@ runs: run: | cd "${{ inputs.changelog_path }}" + FILE_PREFIX="${{ inputs.file_prefix }}" SOURCE_LANG="${{ inputs.source_lang }}" TARGET_LANG="${{ inputs.target_lang }}" @@ -93,17 +99,18 @@ runs: import re from packaging import version + file_prefix = os.environ.get('FILE_PREFIX', 'CHANGELOG-v') source_lang = os.environ.get('SOURCE_LANG', 'ru') target_lang = os.environ.get('TARGET_LANG', 'en') # Определить паттерн файлов на основе source_lang if source_lang == 'en': # Для английского ищем файлы без языкового суффикса - file_pattern = re.compile(r'^v(\d+\.\d+\.\d+)\.yml$') + file_pattern = re.compile(rf'^{re.escape(file_prefix)}(\d+\.\d+\.\d+)\.yml$') source_suffix = '' else: # Для других языков ищем файлы с языковым суффиксом - file_pattern = re.compile(rf'^v(\d+\.\d+\.\d+)\.{re.escape(source_lang)}\.yml$') + file_pattern = re.compile(rf'^{re.escape(file_prefix)}(\d+\.\d+\.\d+)\.{re.escape(source_lang)}\.yml$') source_suffix = f'.{source_lang}' files = [f for f in os.listdir('.') if file_pattern.match(f)] @@ -124,13 +131,13 @@ runs: latest_version, source_file = versions[0] # Определить имя целевого файла - version_str = f"v{latest_version}" + version_str = f"{latest_version}" if target_lang == 'en': # Для английского целевой файл без языкового суффикса - target_file = f"{version_str}.yml" + target_file = f"{file_prefix}{version_str}.yml" else: # Для других языков целевой файл с языковым суффиксом - target_file = f"{version_str}.{target_lang}.yml" + target_file = f"{file_prefix}{version_str}.{target_lang}.yml" # Проверить, существует ли уже целевой файл if os.path.exists(target_file): @@ -139,10 +146,10 @@ runs: github_output = os.environ.get('GITHUB_OUTPUT', '/dev/stdout') with open(github_output, 'a') as f: - f.write(f"version={version_str}\n") + f.write(f"version=v{version_str}\n") f.write(f"source_file={source_file}\n") f.write(f"target_file={target_file}\n") - print(f"Latest version: {version_str}, source: {source_file}, target: {target_file}") + print(f"Latest version: v{version_str}, source: {source_file}, target: {target_file}") EOF continue-on-error: true From cc5e6c7a53a7730503230a6862e2b2655cbad370 Mon Sep 17 00:00:00 2001 From: Maksim Fedotov Date: Wed, 18 Feb 2026 11:42:23 +0300 Subject: [PATCH 3/9] fix exports Signed-off-by: Maksim Fedotov --- translate-changelog/action.yml | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/translate-changelog/action.yml b/translate-changelog/action.yml index 99a66f5..fafa935 100644 --- a/translate-changelog/action.yml +++ b/translate-changelog/action.yml @@ -88,21 +88,21 @@ runs: shell: bash run: | cd "${{ inputs.changelog_path }}" - - FILE_PREFIX="${{ inputs.file_prefix }}" - SOURCE_LANG="${{ inputs.source_lang }}" - TARGET_LANG="${{ inputs.target_lang }}" - + + export FILE_PREFIX="${{ inputs.file_prefix }}" + export SOURCE_LANG="${{ inputs.source_lang }}" + export TARGET_LANG="${{ inputs.target_lang }}" + # Найти все файлы исходного языка и отсортировать по semver python3 << 'EOF' import os import re from packaging import version - + file_prefix = os.environ.get('FILE_PREFIX', 'CHANGELOG-v') source_lang = os.environ.get('SOURCE_LANG', 'ru') target_lang = os.environ.get('TARGET_LANG', 'en') - + # Определить паттерн файлов на основе source_lang if source_lang == 'en': # Для английского ищем файлы без языкового суффикса @@ -112,24 +112,24 @@ runs: # Для других языков ищем файлы с языковым суффиксом file_pattern = re.compile(rf'^{re.escape(file_prefix)}(\d+\.\d+\.\d+)\.{re.escape(source_lang)}\.yml$') source_suffix = f'.{source_lang}' - + files = [f for f in os.listdir('.') if file_pattern.match(f)] - + # Извлечь версии из имен файлов versions = [] for f in files: match = file_pattern.match(f) if match: versions.append((version.parse(match.group(1)), f)) - + if not versions: print(f"No changelog files found for source language: {source_lang}") exit(1) - + # Отсортировать по версии (последняя версия первая) versions.sort(reverse=True) latest_version, source_file = versions[0] - + # Определить имя целевого файла version_str = f"{latest_version}" if target_lang == 'en': @@ -138,12 +138,12 @@ runs: else: # Для других языков целевой файл с языковым суффиксом target_file = f"{file_prefix}{version_str}.{target_lang}.yml" - + # Проверить, существует ли уже целевой файл if os.path.exists(target_file): print(f"Target file {target_file} already exists, skipping") exit(0) - + github_output = os.environ.get('GITHUB_OUTPUT', '/dev/stdout') with open(github_output, 'a') as f: f.write(f"version=v{version_str}\n") From 8daf8417e56adead01f8319c10558286b949f9ac Mon Sep 17 00:00:00 2001 From: Maksim Fedotov Date: Wed, 18 Feb 2026 18:32:18 +0300 Subject: [PATCH 4/9] translate only values and make separate branch Signed-off-by: Maksim Fedotov --- translate-changelog/action.yml | 126 +++++++++++++++++++-------------- 1 file changed, 72 insertions(+), 54 deletions(-) diff --git a/translate-changelog/action.yml b/translate-changelog/action.yml index fafa935..278e8ac 100644 --- a/translate-changelog/action.yml +++ b/translate-changelog/action.yml @@ -88,21 +88,21 @@ runs: shell: bash run: | cd "${{ inputs.changelog_path }}" - + export FILE_PREFIX="${{ inputs.file_prefix }}" export SOURCE_LANG="${{ inputs.source_lang }}" export TARGET_LANG="${{ inputs.target_lang }}" - + # Найти все файлы исходного языка и отсортировать по semver python3 << 'EOF' import os import re from packaging import version - + file_prefix = os.environ.get('FILE_PREFIX', 'CHANGELOG-v') source_lang = os.environ.get('SOURCE_LANG', 'ru') target_lang = os.environ.get('TARGET_LANG', 'en') - + # Определить паттерн файлов на основе source_lang if source_lang == 'en': # Для английского ищем файлы без языкового суффикса @@ -112,24 +112,24 @@ runs: # Для других языков ищем файлы с языковым суффиксом file_pattern = re.compile(rf'^{re.escape(file_prefix)}(\d+\.\d+\.\d+)\.{re.escape(source_lang)}\.yml$') source_suffix = f'.{source_lang}' - + files = [f for f in os.listdir('.') if file_pattern.match(f)] - + # Извлечь версии из имен файлов versions = [] for f in files: match = file_pattern.match(f) if match: versions.append((version.parse(match.group(1)), f)) - + if not versions: print(f"No changelog files found for source language: {source_lang}") exit(1) - + # Отсортировать по версии (последняя версия первая) versions.sort(reverse=True) latest_version, source_file = versions[0] - + # Определить имя целевого файла version_str = f"{latest_version}" if target_lang == 'en': @@ -138,12 +138,12 @@ runs: else: # Для других языков целевой файл с языковым суффиксом target_file = f"{file_prefix}{version_str}.{target_lang}.yml" - + # Проверить, существует ли уже целевой файл if os.path.exists(target_file): print(f"Target file {target_file} already exists, skipping") exit(0) - + github_output = os.environ.get('GITHUB_OUTPUT', '/dev/stdout') with open(github_output, 'a') as f: f.write(f"version=v{version_str}\n") @@ -168,48 +168,66 @@ runs: python3 << 'EOF' from deep_translator import GoogleTranslator import os + import yaml source_file = os.environ['SOURCE_FILE'] target_file = os.environ['TARGET_FILE'] source_lang = os.environ['SOURCE_LANG'] target_lang = os.environ['TARGET_LANG'] - # Загрузить исходный файл построчно для сохранения форматирования + # Загрузить YAML файл with open(source_file, 'r', encoding='utf-8') as f: - source_lines = f.readlines() + data = yaml.safe_load(f) - # Перевести содержимое построчно + # Инициализировать переводчик translator = GoogleTranslator(source=source_lang, target=target_lang) - # Сохранить с сохранением оригинального форматирования + def translate_value(value): + """Рекурсивно переводит значения в структуре данных""" + if isinstance(value, str): + # Переводим строку + try: + return translator.translate(value) + except Exception as e: + print(f"Warning: Failed to translate '{value[:50]}...': {e}") + return value + elif isinstance(value, dict): + # Рекурсивно обрабатываем словарь (ключи остаются без изменений) + return {key: translate_value(val) for key, val in value.items()} + elif isinstance(value, list): + # Рекурсивно обрабатываем список + return [translate_value(item) for item in value] + else: + # Другие типы (числа, bool, None) возвращаем без изменений + return value + + # Перевести все значения в структуре + translated_data = translate_value(data) + + # Сохранить переведенный YAML with open(target_file, 'w', encoding='utf-8') as f: - for line in source_lines: - # Пропустить пустые строки - if not line.strip(): - f.write(line) - continue - - # Сохранить оригинальные отступы - indent = len(line) - len(line.lstrip()) - content = line.strip() - - # Перевести содержимое строки - translated_content = translator.translate(content) - - # Записать переведенную строку с сохранением отступов - f.write(' ' * indent + translated_content + '\n') + yaml.dump(translated_data, f, allow_unicode=True, default_flow_style=False, sort_keys=False) print(f"Translated {source_file} ({source_lang}) to {target_file} ({target_lang})") EOF - - name: Get current branch name + - name: Get current branch name and create translation branch if: steps.check_changes.outputs.has_changes == 'true' && steps.find_changelog.outcome == 'success' id: get_branch shell: bash run: | - BRANCH_NAME=$(git rev-parse --abbrev-ref HEAD) - echo "branch=$BRANCH_NAME" >> $GITHUB_OUTPUT - echo "Current branch: $BRANCH_NAME" + VERSION="${{ steps.find_changelog.outputs.version }}" + SOURCE_LANG="${{ inputs.source_lang }}" + TARGET_LANG="${{ inputs.target_lang }}" + + # Создать имя новой ветки для перевода + TRANSLATION_BRANCH="translation/${SOURCE_LANG}-to-${TARGET_LANG}/${VERSION}" + + # Создать и переключиться на новую ветку + git checkout -b "$TRANSLATION_BRANCH" + + echo "branch=$TRANSLATION_BRANCH" >> $GITHUB_OUTPUT + echo "Created and switched to branch: $TRANSLATION_BRANCH" - name: Commit translated changelog if: steps.check_changes.outputs.has_changes == 'true' && steps.find_changelog.outcome == 'success' @@ -241,6 +259,7 @@ runs: TARGET_LANG="${{ inputs.target_lang }}" SOURCE_FILE="${{ steps.find_changelog.outputs.source_file }}" TARGET_FILE="${{ steps.find_changelog.outputs.target_file }}" + CHANGELOG_PATH="${{ inputs.changelog_path }}" # Проверить, существует ли уже PR для этой ветки EXISTING_PR=$(gh pr list --head "$BRANCH_NAME" --base "$BASE_BRANCH" --json number --jq '.[0].number' 2>/dev/null || echo "") @@ -263,32 +282,31 @@ runs: *) TARGET_LANG_NAME="$TARGET_LANG" ;; esac - # Создать временный файл для body PR + # Создать PR body файл PR_BODY_FILE=$(mktemp) - cat > "$PR_BODY_FILE" << EOF - ## Changelog $VERSION - - This PR contains the $TARGET_LANG_NAME translation of the $SOURCE_LANG_NAME changelog for version $VERSION. - - **Source file:** `${{ inputs.changelog_path }}/${SOURCE_FILE}` - **Translated file:** `${{ inputs.changelog_path }}/${TARGET_FILE}` - -
- Changelog content - - \`\`\`yaml - $(cat "${{ inputs.changelog_path }}/${TARGET_FILE}") - \`\`\` - -
- EOF - + + echo "## Changelog $VERSION" > "$PR_BODY_FILE" + echo "" >> "$PR_BODY_FILE" + echo "This PR contains the $TARGET_LANG_NAME translation of the $SOURCE_LANG_NAME changelog for version $VERSION." >> "$PR_BODY_FILE" + echo "" >> "$PR_BODY_FILE" + echo "**Source file:** \`$CHANGELOG_PATH/$SOURCE_FILE\`" >> "$PR_BODY_FILE" + echo "**Translated file:** \`$CHANGELOG_PATH/$TARGET_FILE\`" >> "$PR_BODY_FILE" + echo "" >> "$PR_BODY_FILE" + echo "
" >> "$PR_BODY_FILE" + echo "Changelog content" >> "$PR_BODY_FILE" + echo "" >> "$PR_BODY_FILE" + echo "\`\`\`yaml" >> "$PR_BODY_FILE" + cat "$CHANGELOG_PATH/$TARGET_FILE" >> "$PR_BODY_FILE" + echo "\`\`\`" >> "$PR_BODY_FILE" + echo "" >> "$PR_BODY_FILE" + echo "
" >> "$PR_BODY_FILE" + # Создать PR gh pr create \ --base "$BASE_BRANCH" \ --head "$BRANCH_NAME" \ --title "$VERSION" \ --body-file "$PR_BODY_FILE" - + # Удалить временный файл rm -f "$PR_BODY_FILE" From b5889febe52048e94f1ad1cc651259b255a2aa51 Mon Sep 17 00:00:00 2001 From: Maksim Fedotov Date: Wed, 18 Feb 2026 20:19:13 +0300 Subject: [PATCH 5/9] rewrite files if they exist Signed-off-by: Maksim Fedotov --- translate-changelog/action.yml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/translate-changelog/action.yml b/translate-changelog/action.yml index 278e8ac..a269833 100644 --- a/translate-changelog/action.yml +++ b/translate-changelog/action.yml @@ -138,12 +138,7 @@ runs: else: # Для других языков целевой файл с языковым суффиксом target_file = f"{file_prefix}{version_str}.{target_lang}.yml" - - # Проверить, существует ли уже целевой файл - if os.path.exists(target_file): - print(f"Target file {target_file} already exists, skipping") - exit(0) - + github_output = os.environ.get('GITHUB_OUTPUT', '/dev/stdout') with open(github_output, 'a') as f: f.write(f"version=v{version_str}\n") From cb236bc5eb2dc48df56c5681320b95fc4a0809f0 Mon Sep 17 00:00:00 2001 From: Maksim Fedotov Date: Thu, 19 Feb 2026 15:53:57 +0300 Subject: [PATCH 6/9] require github token Signed-off-by: Maksim Fedotov --- translate-changelog/action.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/translate-changelog/action.yml b/translate-changelog/action.yml index a269833..9677add 100644 --- a/translate-changelog/action.yml +++ b/translate-changelog/action.yml @@ -18,6 +18,9 @@ inputs: description: 'Target language code (en or ru)' required: false default: 'en' + github_token: + description: 'GitHub token for creating pull requests' + required: false python_version: description: 'Python version to use' required: false @@ -245,7 +248,7 @@ runs: if: steps.check_changes.outputs.has_changes == 'true' && steps.find_changelog.outcome == 'success' shell: bash env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ inputs.github_token }} run: | BRANCH_NAME="${{ steps.get_branch.outputs.branch }}" BASE_BRANCH="${{ inputs.base_branch }}" From d71831112c4157951375d8f1e222aabfbee96cbe Mon Sep 17 00:00:00 2001 From: Maksim Fedotov Date: Thu, 19 Feb 2026 16:26:05 +0300 Subject: [PATCH 7/9] force push branches Signed-off-by: Maksim Fedotov --- translate-changelog/action.yml | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/translate-changelog/action.yml b/translate-changelog/action.yml index 9677add..1984a8f 100644 --- a/translate-changelog/action.yml +++ b/translate-changelog/action.yml @@ -217,15 +217,27 @@ runs: VERSION="${{ steps.find_changelog.outputs.version }}" SOURCE_LANG="${{ inputs.source_lang }}" TARGET_LANG="${{ inputs.target_lang }}" - + # Создать имя новой ветки для перевода TRANSLATION_BRANCH="translation/${SOURCE_LANG}-to-${TARGET_LANG}/${VERSION}" - - # Создать и переключиться на новую ветку - git checkout -b "$TRANSLATION_BRANCH" - + + # Проверить существует ли ветка локально или на remote + if git show-ref --verify --quiet "refs/heads/$TRANSLATION_BRANCH"; then + # Ветка существует локально, переключаемся на нее + git checkout "$TRANSLATION_BRANCH" + echo "Switched to existing local branch: $TRANSLATION_BRANCH" + elif git ls-remote --heads origin "$TRANSLATION_BRANCH" | grep -q "$TRANSLATION_BRANCH"; then + # Ветка существует на remote, получаем ее + git fetch origin "$TRANSLATION_BRANCH" + git checkout -b "$TRANSLATION_BRANCH" "origin/$TRANSLATION_BRANCH" + echo "Checked out existing remote branch: $TRANSLATION_BRANCH" + else + # Ветка не существует, создаем новую + git checkout -b "$TRANSLATION_BRANCH" + echo "Created new branch: $TRANSLATION_BRANCH" + fi + echo "branch=$TRANSLATION_BRANCH" >> $GITHUB_OUTPUT - echo "Created and switched to branch: $TRANSLATION_BRANCH" - name: Commit translated changelog if: steps.check_changes.outputs.has_changes == 'true' && steps.find_changelog.outcome == 'success' @@ -237,12 +249,12 @@ runs: git config --local user.email "action@github.com" git config --local user.name "GitHub Action" - + git add "${{ inputs.changelog_path }}/" git commit -m "Translate changelog ${VERSION} from ${SOURCE_LANG} to ${TARGET_LANG}" - - # Пушим коммит в текущую ветку - git push origin HEAD + + # Пушим коммит в текущую ветку (force push, так как обновляем перевод) + git push -f origin HEAD - name: Create Pull Request using GitHub CLI if: steps.check_changes.outputs.has_changes == 'true' && steps.find_changelog.outcome == 'success' From c56cdd2500fb5e5f91f7034e25a5b7c3caf65150 Mon Sep 17 00:00:00 2001 From: Maksim Fedotov Date: Thu, 19 Feb 2026 16:30:41 +0300 Subject: [PATCH 8/9] swap translation and checkout Signed-off-by: Maksim Fedotov --- translate-changelog/action.yml | 60 +++++++++++++++++----------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/translate-changelog/action.yml b/translate-changelog/action.yml index 1984a8f..664e0a2 100644 --- a/translate-changelog/action.yml +++ b/translate-changelog/action.yml @@ -151,6 +151,36 @@ runs: EOF continue-on-error: true + - name: Get current branch name and create translation branch + if: steps.check_changes.outputs.has_changes == 'true' && steps.find_changelog.outcome == 'success' + id: get_branch + shell: bash + run: | + VERSION="${{ steps.find_changelog.outputs.version }}" + SOURCE_LANG="${{ inputs.source_lang }}" + TARGET_LANG="${{ inputs.target_lang }}" + + # Создать имя новой ветки для перевода + TRANSLATION_BRANCH="translation/${SOURCE_LANG}-to-${TARGET_LANG}/${VERSION}" + + # Проверить существует ли ветка локально или на remote + if git show-ref --verify --quiet "refs/heads/$TRANSLATION_BRANCH"; then + # Ветка существует локально, переключаемся на нее + git checkout "$TRANSLATION_BRANCH" + echo "Switched to existing local branch: $TRANSLATION_BRANCH" + elif git ls-remote --heads origin "$TRANSLATION_BRANCH" | grep -q "$TRANSLATION_BRANCH"; then + # Ветка существует на remote, получаем ее + git fetch origin "$TRANSLATION_BRANCH" + git checkout -b "$TRANSLATION_BRANCH" "origin/$TRANSLATION_BRANCH" + echo "Checked out existing remote branch: $TRANSLATION_BRANCH" + else + # Ветка не существует, создаем новую + git checkout -b "$TRANSLATION_BRANCH" + echo "Created new branch: $TRANSLATION_BRANCH" + fi + + echo "branch=$TRANSLATION_BRANCH" >> $GITHUB_OUTPUT + - name: Translate changelog if: steps.check_changes.outputs.has_changes == 'true' && steps.find_changelog.outcome == 'success' id: translate @@ -209,36 +239,6 @@ runs: print(f"Translated {source_file} ({source_lang}) to {target_file} ({target_lang})") EOF - - name: Get current branch name and create translation branch - if: steps.check_changes.outputs.has_changes == 'true' && steps.find_changelog.outcome == 'success' - id: get_branch - shell: bash - run: | - VERSION="${{ steps.find_changelog.outputs.version }}" - SOURCE_LANG="${{ inputs.source_lang }}" - TARGET_LANG="${{ inputs.target_lang }}" - - # Создать имя новой ветки для перевода - TRANSLATION_BRANCH="translation/${SOURCE_LANG}-to-${TARGET_LANG}/${VERSION}" - - # Проверить существует ли ветка локально или на remote - if git show-ref --verify --quiet "refs/heads/$TRANSLATION_BRANCH"; then - # Ветка существует локально, переключаемся на нее - git checkout "$TRANSLATION_BRANCH" - echo "Switched to existing local branch: $TRANSLATION_BRANCH" - elif git ls-remote --heads origin "$TRANSLATION_BRANCH" | grep -q "$TRANSLATION_BRANCH"; then - # Ветка существует на remote, получаем ее - git fetch origin "$TRANSLATION_BRANCH" - git checkout -b "$TRANSLATION_BRANCH" "origin/$TRANSLATION_BRANCH" - echo "Checked out existing remote branch: $TRANSLATION_BRANCH" - else - # Ветка не существует, создаем новую - git checkout -b "$TRANSLATION_BRANCH" - echo "Created new branch: $TRANSLATION_BRANCH" - fi - - echo "branch=$TRANSLATION_BRANCH" >> $GITHUB_OUTPUT - - name: Commit translated changelog if: steps.check_changes.outputs.has_changes == 'true' && steps.find_changelog.outcome == 'success' shell: bash From 1544197d98cb1ebdc7a388f44aef89ec9f73180c Mon Sep 17 00:00:00 2001 From: Maksim Fedotov Date: Fri, 20 Feb 2026 10:36:34 +0300 Subject: [PATCH 9/9] move to peter-evans/create-pull-request Signed-off-by: Maksim Fedotov --- translate-changelog/action.yml | 102 ++++++++------------------------- 1 file changed, 23 insertions(+), 79 deletions(-) diff --git a/translate-changelog/action.yml b/translate-changelog/action.yml index 664e0a2..41bdad3 100644 --- a/translate-changelog/action.yml +++ b/translate-changelog/action.yml @@ -239,84 +239,28 @@ runs: print(f"Translated {source_file} ({source_lang}) to {target_file} ({target_lang})") EOF - - name: Commit translated changelog + - name: Create Pull Request if: steps.check_changes.outputs.has_changes == 'true' && steps.find_changelog.outcome == 'success' - shell: bash - run: | - VERSION="${{ steps.find_changelog.outputs.version }}" - SOURCE_LANG="${{ inputs.source_lang }}" - TARGET_LANG="${{ inputs.target_lang }}" - - git config --local user.email "action@github.com" - git config --local user.name "GitHub Action" - - git add "${{ inputs.changelog_path }}/" - git commit -m "Translate changelog ${VERSION} from ${SOURCE_LANG} to ${TARGET_LANG}" - - # Пушим коммит в текущую ветку (force push, так как обновляем перевод) - git push -f origin HEAD - - - name: Create Pull Request using GitHub CLI - if: steps.check_changes.outputs.has_changes == 'true' && steps.find_changelog.outcome == 'success' - shell: bash - env: - GITHUB_TOKEN: ${{ inputs.github_token }} - run: | - BRANCH_NAME="${{ steps.get_branch.outputs.branch }}" - BASE_BRANCH="${{ inputs.base_branch }}" - VERSION="${{ steps.find_changelog.outputs.version }}" - SOURCE_LANG="${{ inputs.source_lang }}" - TARGET_LANG="${{ inputs.target_lang }}" - SOURCE_FILE="${{ steps.find_changelog.outputs.source_file }}" - TARGET_FILE="${{ steps.find_changelog.outputs.target_file }}" - CHANGELOG_PATH="${{ inputs.changelog_path }}" - - # Проверить, существует ли уже PR для этой ветки - EXISTING_PR=$(gh pr list --head "$BRANCH_NAME" --base "$BASE_BRANCH" --json number --jq '.[0].number' 2>/dev/null || echo "") - - if [ -n "$EXISTING_PR" ] && [ "$EXISTING_PR" != "null" ]; then - echo "PR #$EXISTING_PR already exists for branch $BRANCH_NAME" - exit 0 - fi - - # Определить полные названия языков для PR - case "$SOURCE_LANG" in - ru) SOURCE_LANG_NAME="Russian" ;; - en) SOURCE_LANG_NAME="English" ;; - *) SOURCE_LANG_NAME="$SOURCE_LANG" ;; - esac - - case "$TARGET_LANG" in - ru) TARGET_LANG_NAME="Russian" ;; - en) TARGET_LANG_NAME="English" ;; - *) TARGET_LANG_NAME="$TARGET_LANG" ;; - esac + uses: peter-evans/create-pull-request@v7 + with: + token: ${{ inputs.github_token }} + commit-message: Translate changelog ${{ steps.find_changelog.outputs.version }} from ${{ inputs.source_lang }} to ${{ inputs.target_lang }} + branch: translation/${{ inputs.source_lang }}-to-${{ inputs.target_lang }}/${{ steps.find_changelog.outputs.version }} + delete-branch: false + title: ${{ steps.find_changelog.outputs.version }} + body: | + ## Changelog ${{ steps.find_changelog.outputs.version }} + + This PR contains the ${{ inputs.target_lang == 'ru' && 'Russian' || 'English' }} translation of the ${{ inputs.source_lang == 'ru' && 'Russian' || 'English' }} changelog for version ${{ steps.find_changelog.outputs.version }}. + + **Source file:** `${{ inputs.changelog_path }}/${{ steps.find_changelog.outputs.source_file }}` + **Translated file:** `${{ inputs.changelog_path }}/${{ steps.find_changelog.outputs.target_file }}` + +
+ Changelog content + + The translated changelog file has been generated and is ready for review. + +
+ base: ${{ inputs.base_branch }} - # Создать PR body файл - PR_BODY_FILE=$(mktemp) - - echo "## Changelog $VERSION" > "$PR_BODY_FILE" - echo "" >> "$PR_BODY_FILE" - echo "This PR contains the $TARGET_LANG_NAME translation of the $SOURCE_LANG_NAME changelog for version $VERSION." >> "$PR_BODY_FILE" - echo "" >> "$PR_BODY_FILE" - echo "**Source file:** \`$CHANGELOG_PATH/$SOURCE_FILE\`" >> "$PR_BODY_FILE" - echo "**Translated file:** \`$CHANGELOG_PATH/$TARGET_FILE\`" >> "$PR_BODY_FILE" - echo "" >> "$PR_BODY_FILE" - echo "
" >> "$PR_BODY_FILE" - echo "Changelog content" >> "$PR_BODY_FILE" - echo "" >> "$PR_BODY_FILE" - echo "\`\`\`yaml" >> "$PR_BODY_FILE" - cat "$CHANGELOG_PATH/$TARGET_FILE" >> "$PR_BODY_FILE" - echo "\`\`\`" >> "$PR_BODY_FILE" - echo "" >> "$PR_BODY_FILE" - echo "
" >> "$PR_BODY_FILE" - - # Создать PR - gh pr create \ - --base "$BASE_BRANCH" \ - --head "$BRANCH_NAME" \ - --title "$VERSION" \ - --body-file "$PR_BODY_FILE" - - # Удалить временный файл - rm -f "$PR_BODY_FILE"