mirror of
https://github.com/opf/openproject.git
synced 2026-06-14 03:30:14 +00:00
Add workflow to test seeding in all locales
There is a `script/i18n/test_seed_all_locales` to test seeding in all
locales, or one locale.
A GitHub action tests all locales seeding every week on Sunday at 2 AM
UTC. It can also be triggered manually.
Example:
script/i18n/test_seed_all_locales # Seed all locales sequentially
script/i18n/test_seed_all_locales --list # Output available locales as JSON
script/i18n/test_seed_all_locales zh-CN # Seed a single locale
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
name: Test seeding in all locales
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 2 * * 0' # Weekly on Sunday at 2 AM UTC
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
ref:
|
||||
description: 'Git ref to test (branch, tag, or SHA). Defaults to latest release branch.'
|
||||
required: false
|
||||
type: string
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
prepare:
|
||||
if: github.repository == 'opf/openproject'
|
||||
name: Prepare
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
locales: ${{ steps.list.outputs.locales }}
|
||||
ref: ${{ steps.use_input_or_find_latest_release.outputs.ref }}
|
||||
steps:
|
||||
- name: Determine git ref to test seeding in
|
||||
id: use_input_or_find_latest_release
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
INPUT_REF: ${{ inputs.ref }}
|
||||
run: |
|
||||
if [ -n "$INPUT_REF" ]; then
|
||||
echo "ref=$INPUT_REF" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
BRANCH=$(gh api repos/opf/openproject/branches --paginate --jq '.[].name' | grep '^release/' | sort --version-sort | tail -1)
|
||||
if [ -z "$BRANCH" ]; then
|
||||
echo "Error: no release branch found"
|
||||
exit 1
|
||||
fi
|
||||
echo "Found latest release branch: $BRANCH"
|
||||
echo "ref=$BRANCH" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ steps.use_input_or_find_latest_release.outputs.ref }}
|
||||
|
||||
- name: List available locales
|
||||
id: list
|
||||
run: |
|
||||
locales=$(ruby script/i18n/test_seed_all_locales --list)
|
||||
echo "locales=$locales" >> "$GITHUB_OUTPUT"
|
||||
|
||||
seed:
|
||||
needs: prepare
|
||||
if: github.repository == 'opf/openproject'
|
||||
name: Seed ${{ matrix.locale }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
locale: ${{ fromJson(needs.prepare.outputs.locales) }}
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:17
|
||||
env:
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
ports:
|
||||
- 5432:5432
|
||||
options: >-
|
||||
--health-cmd pg_isready
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
env:
|
||||
DATABASE_URL: postgres://postgres:postgres@localhost:5432/openproject_seed_test
|
||||
RAILS_ENV: development
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ needs.prepare.outputs.ref }}
|
||||
|
||||
- name: Install system dependencies
|
||||
run: sudo apt-get update && sudo apt-get install -y libpq-dev
|
||||
|
||||
- name: Setup Ruby
|
||||
uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
bundler-cache: true
|
||||
|
||||
- name: Configure database
|
||||
run: |
|
||||
cat > config/database.yml <<'EOF'
|
||||
development:
|
||||
url: <%= ENV["DATABASE_URL"] %>
|
||||
EOF
|
||||
|
||||
- name: Seed locale ${{ matrix.locale }}
|
||||
run: ruby script/i18n/test_seed_all_locales ${{ matrix.locale }}
|
||||
Executable
+185
@@ -0,0 +1,185 @@
|
||||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Tests that seeding works in all available locales.
|
||||
#
|
||||
# Usage:
|
||||
# script/i18n/test_seed_all_locales # Seed all locales sequentially
|
||||
# script/i18n/test_seed_all_locales --list # Output available locales as JSON
|
||||
# script/i18n/test_seed_all_locales zh-CN # Seed a single locale
|
||||
|
||||
require "pathname"
|
||||
require "json"
|
||||
|
||||
class SeedAllLocales
|
||||
class << self
|
||||
def call(args)
|
||||
case args.first
|
||||
when "-h", "--help"
|
||||
print_usage
|
||||
when "--list"
|
||||
puts JSON.generate(available_locales)
|
||||
when nil
|
||||
seed_all_locales
|
||||
else
|
||||
locale = args.first
|
||||
validate_locale!(locale)
|
||||
seed_one_locale(locale)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def print_usage
|
||||
puts <<~USAGE
|
||||
Usage: #{$0} [OPTIONS] [LOCALE]
|
||||
|
||||
Tests that seeding works in all available locales.
|
||||
Uses the current development database.
|
||||
|
||||
Options:
|
||||
-h, --help Show this help message
|
||||
--list Output available locales as JSON array
|
||||
|
||||
Arguments:
|
||||
LOCALE Seed a single locale (e.g. zh-CN, de, en)
|
||||
|
||||
Examples:
|
||||
#{$0} # Seed all locales sequentially
|
||||
#{$0} --list # Output available locales as JSON
|
||||
#{$0} zh-CN # Seed a single locale
|
||||
USAGE
|
||||
end
|
||||
|
||||
def validate_locale!(locale)
|
||||
return if available_locales.include?(locale)
|
||||
|
||||
warn "Error: unknown locale '#{locale}'"
|
||||
warn "Available locales: #{available_locales.join(', ')}"
|
||||
exit 1
|
||||
end
|
||||
|
||||
def available_locales
|
||||
@available_locales ||=
|
||||
rails_root.glob("config/locales/**/*.yml")
|
||||
.map { |f| f.basename.to_s.split(".", 2).first }
|
||||
.reject { |l| l.start_with?("js-") || l == "lol" }
|
||||
.uniq
|
||||
.sort
|
||||
end
|
||||
|
||||
def rails_root
|
||||
@rails_root ||=
|
||||
Pathname.new(__dir__)
|
||||
.ascend
|
||||
.find { |dir| dir.join("Gemfile").exist? }
|
||||
.tap { |dir| raise "Unable to find Rails root directory (looking up from #{__dir__})" if dir.nil? }
|
||||
end
|
||||
|
||||
# Runs all locales sequentially and reports all failures at the end.
|
||||
def seed_all_locales
|
||||
locales = available_locales
|
||||
puts "Testing seeding in #{locales.count} locales"
|
||||
puts "Locales: #{locales.join(', ')}"
|
||||
puts
|
||||
|
||||
unless setup_schema
|
||||
puts "ERROR: Database schema setup failed. Cannot continue."
|
||||
exit 1
|
||||
end
|
||||
|
||||
results = {}
|
||||
locales.each_with_index do |locale, index|
|
||||
puts
|
||||
puts "=== [#{index + 1}/#{locales.count}] Seeding locale: #{locale} ==="
|
||||
success = reset_and_seed(locale)
|
||||
results[locale] = success
|
||||
status = success ? "OK" : "FAILED"
|
||||
puts "--- #{locale}: #{status} ---"
|
||||
end
|
||||
|
||||
print_summary(results)
|
||||
end
|
||||
|
||||
# Runs a single locale and exits with appropriate status code.
|
||||
def seed_one_locale(locale)
|
||||
puts "=== Seeding locale: #{locale} ==="
|
||||
|
||||
terminate_db_connections
|
||||
unless run("bin/rails", "db:drop", "db:create", "db:migrate")
|
||||
puts "--- #{locale}: FAILED (database setup) ---"
|
||||
exit 1
|
||||
end
|
||||
|
||||
unless run("bin/rails", "db:seed", env: { "OPENPROJECT_SEED_LOCALE" => locale })
|
||||
puts "--- #{locale}: FAILED (seeding) ---"
|
||||
exit 1
|
||||
end
|
||||
|
||||
puts "--- #{locale}: OK ---"
|
||||
end
|
||||
|
||||
def setup_schema
|
||||
puts "=== Setting up database schema ==="
|
||||
terminate_db_connections
|
||||
run("bin/rails", "db:drop", "db:create", "db:migrate", "db:schema:dump")
|
||||
end
|
||||
|
||||
def reset_and_seed(locale)
|
||||
terminate_db_connections
|
||||
run("bin/rails", "db:drop", "db:create", "db:schema:load") &&
|
||||
run("bin/rails", "db:seed", env: { "OPENPROJECT_SEED_LOCALE" => locale })
|
||||
end
|
||||
|
||||
def terminate_db_connections
|
||||
db_name = database_name
|
||||
return unless db_name
|
||||
|
||||
run("psql", "-d", "postgres", "-c",
|
||||
"SELECT pg_terminate_backend(pid) FROM pg_stat_activity " \
|
||||
"WHERE datname = '#{db_name}' AND pid <> pg_backend_pid()")
|
||||
end
|
||||
|
||||
def database_name
|
||||
@database_name ||= begin
|
||||
require "uri"
|
||||
db_url = ENV.fetch("DATABASE_URL", nil)
|
||||
if db_url
|
||||
URI.parse(db_url).path.delete_prefix("/")
|
||||
else
|
||||
# Fall back to asking Rails; use .split.last to ignore any
|
||||
# extra output from initializers (e.g. REPL commands messages)
|
||||
`bin/rails runner "print ActiveRecord::Base.connection_db_config.database"`.split.last
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def run(*cmd, env: {})
|
||||
env_str = env.map { |k, v| "#{k}=#{v}" }.join(" ")
|
||||
puts " $ #{env_str} #{cmd.join(' ')}".strip
|
||||
system(env, *cmd, chdir: rails_root.to_s)
|
||||
end
|
||||
|
||||
def print_summary(results)
|
||||
failed = results.reject { |_, success| success }
|
||||
succeeded = results.select { |_, success| success }
|
||||
|
||||
puts <<~SUMMARY
|
||||
================================
|
||||
SUMMARY: #{succeeded.count} succeeded, #{failed.count} failed out of #{results.count} locales
|
||||
================================
|
||||
SUMMARY
|
||||
|
||||
if failed.any?
|
||||
puts <<~FAILED
|
||||
|
||||
Failed locales:
|
||||
#{failed.keys.map { |locale| " - #{locale}" }.join("\n")}
|
||||
FAILED
|
||||
exit 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
SeedAllLocales.call(ARGV)
|
||||
Reference in New Issue
Block a user