diff --git a/.github/workflows/cockroach.yml b/.github/workflows/cockroach.yml index a6087cac3..6466785b3 100644 --- a/.github/workflows/cockroach.yml +++ b/.github/workflows/cockroach.yml @@ -18,7 +18,7 @@ jobs: steps: - name: Start CockroachDB run: docker run -d -p 26257:26257 cockroachdb/cockroach:latest-v${{ matrix.version }} start-single-node --insecure - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup Perl id: perl uses: shogo82148/actions-setup-perl@v1 diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index b433912a1..500bc8eb4 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -49,7 +49,7 @@ jobs: run: rm -rf /opt/hostedtoolcache - name: Start CockroachDB run: docker run -d -p 26257:26257 cockroachdb/cockroach:latest start-single-node --insecure - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup Perl id: perl uses: shogo82148/actions-setup-perl@v1 diff --git a/.github/workflows/exasol.yml b/.github/workflows/exasol.yml index d10db9d22..1f33fd39a 100644 --- a/.github/workflows/exasol.yml +++ b/.github/workflows/exasol.yml @@ -20,7 +20,7 @@ jobs: ports: [ 8563 ] options: --privileged steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup Clients run: .github/ubuntu/exasol.sh - name: Setup Perl diff --git a/.github/workflows/firebird.yml b/.github/workflows/firebird.yml index 36a5541a4..de7c120ed 100644 --- a/.github/workflows/firebird.yml +++ b/.github/workflows/firebird.yml @@ -29,7 +29,7 @@ jobs: ISC_PASSWORD: nix FIREBIRD_DATABASE: sqitchtest.db steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup Clients run: .github/ubuntu/firebird.sh - name: Setup Perl diff --git a/.github/workflows/mysql.yml b/.github/workflows/mysql.yml index 14cb78ac6..03a7beecf 100644 --- a/.github/workflows/mysql.yml +++ b/.github/workflows/mysql.yml @@ -39,7 +39,7 @@ jobs: ports: [ 3306 ] options: --health-cmd="healthcheck.sh --innodb_initialized || mysqladmin ping --protocol=tcp" --health-interval=5s --health-timeout=2s --health-retries=3 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup Clients run: .github/ubuntu/mysql.sh - name: Setup Perl diff --git a/.github/workflows/oracle.yml b/.github/workflows/oracle.yml index 186501eb3..ea8487ace 100644 --- a/.github/workflows/oracle.yml +++ b/.github/workflows/oracle.yml @@ -40,7 +40,7 @@ jobs: --health-timeout 10s --health-retries 10 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup Clients run: .github/ubuntu/oracle.sh - name: Setup Perl diff --git a/.github/workflows/os.yml b/.github/workflows/os.yml index 6293bb68e..59bea613c 100644 --- a/.github/workflows/os.yml +++ b/.github/workflows/os.yml @@ -12,27 +12,29 @@ jobs: strategy: matrix: include: - - { icon: 🐧, os: ubuntu, name: Linux } - - { icon: 🍎, os: macos, name: macOS } - - { icon: 🪟, os: windows, name: Windows } + - { icon: 🐧, on: ubuntu, name: Linux } + - { icon: 🍎, on: macos, name: macOS } + - { icon: 🪟, on: windows, name: Windows } name: ${{ matrix.icon }} ${{ matrix.name }} - runs-on: ${{ matrix.os }}-latest + runs-on: ${{ matrix.on }}-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup Perl id: perl uses: shogo82148/actions-setup-perl@v1 with: { perl-version: latest } - run: perl -V + - if: runner.os == 'Linux' + name: Install Apt Packages + run: sudo apt-get install -qq aspell-en language-pack-fr language-pack-en language-pack-de language-pack-it - name: Cache CPAN Modules uses: actions/cache@v3 with: path: local key: perl-${{ steps.perl.outputs.perl-hash }} - run: cpm install --verbose --show-build-log-on-failure --no-test --with-recommends ExtUtils::MakeMaker List::MoreUtils::XS - # Remove Locale::TextDomain if https://github.com/gflohr/libintl-perl/issues/7 fixed and released. - - if: ${{ matrix.os == 'windows' }} - run: cpm install --verbose --show-build-log-on-failure --no-test --with-recommends Encode Win32::Console::ANSI Win32API::Net Win32::Locale Win32::ShellQuote DateTime::TimeZone::Local::Win32 Locale::TextDomain@1.31 + - if: runner.os == 'Windows' + run: cpm install --verbose --show-build-log-on-failure --no-test --with-recommends Encode Win32::Console::ANSI Win32API::Net Win32::Locale Win32::ShellQuote DateTime::TimeZone::Local::Win32 - run: cpm install --verbose --show-build-log-on-failure --no-test --with-recommends --cpanfile dist/cpanfile - run: cpm install --verbose --show-build-log-on-failure --no-test --with-recommends Test::Spelling Test::Pod Test::Pod::Coverage - name: prove diff --git a/.github/workflows/perl.yml b/.github/workflows/perl.yml index 22ddac38e..563197298 100644 --- a/.github/workflows/perl.yml +++ b/.github/workflows/perl.yml @@ -19,20 +19,22 @@ jobs: name: 🧅 Perl ${{ matrix.perl }} on ${{ matrix.os[0] }} ${{ matrix.os[1] }} runs-on: ${{ matrix.os[1] }}-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup Perl id: perl uses: shogo82148/actions-setup-perl@v1 with: { perl-version: "${{ matrix.perl }}" } - run: perl -V + - if: runner.os == 'Linux' + name: Install Apt Packages + run: sudo apt-get install -qq language-pack-fr language-pack-en language-pack-de language-pack-it - name: Cache CPAN Modules uses: actions/cache@v3 with: path: local key: perl-${{ steps.perl.outputs.perl-hash }} - # Remove Locale::TextDomain if https://github.com/gflohr/libintl-perl/issues/7 fixed and released. - - if: ${{ matrix.os[1] == 'windows' }} - run: cpm install --verbose --show-build-log-on-failure --no-test --with-recommends Encode Win32::Console::ANSI Win32API::Net Win32::Locale Win32::ShellQuote DateTime::TimeZone::Local::Win32 Locale::TextDomain@1.31 + - if: runner.os == 'Windows' + run: cpm install --verbose --show-build-log-on-failure --no-test --with-recommends Encode Win32::Console::ANSI Win32API::Net Win32::Locale Win32::ShellQuote DateTime::TimeZone::Local::Win32 - run: cpm install --verbose --show-build-log-on-failure --no-test --with-recommends --cpanfile dist/cpanfile - run: cpm install --verbose --show-build-log-on-failure --no-test --with-recommends Test::Spelling Test::Pod Test::Pod::Coverage - name: prove diff --git a/.github/workflows/pg.yml b/.github/workflows/pg.yml index c97c11ba7..39a81739f 100644 --- a/.github/workflows/pg.yml +++ b/.github/workflows/pg.yml @@ -15,7 +15,7 @@ jobs: name: 🐘 Postgres ${{ matrix.pg }} runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup Perl id: perl uses: shogo82148/actions-setup-perl@v1 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9f5a1c1a0..f27f3336c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -10,7 +10,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - name: Check out the repo - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Perl id: perl uses: shogo82148/actions-setup-perl@v1 diff --git a/.github/workflows/snowflake.yml b/.github/workflows/snowflake.yml index 7dc089c56..45ddd26cc 100644 --- a/.github/workflows/snowflake.yml +++ b/.github/workflows/snowflake.yml @@ -11,7 +11,7 @@ jobs: name: ❄️ Snowflake runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup Clients run: .github/ubuntu/snowflake.sh - name: Setup Perl diff --git a/.github/workflows/sqlite.yml b/.github/workflows/sqlite.yml index 4c0b758d1..12e1f4c16 100644 --- a/.github/workflows/sqlite.yml +++ b/.github/workflows/sqlite.yml @@ -16,7 +16,7 @@ jobs: name: 💡 SQLite ${{ matrix.sqlite }} runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup Perl id: perl uses: shogo82148/actions-setup-perl@v1 diff --git a/.github/workflows/vertica.yml b/.github/workflows/vertica.yml index b524bbd3c..5b734dfbd 100644 --- a/.github/workflows/vertica.yml +++ b/.github/workflows/vertica.yml @@ -27,7 +27,7 @@ jobs: image: ${{ matrix.image }}:${{ matrix.version }} ports: [ 5433 ] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup Clients run: .github/ubuntu/vertica.sh - name: Setup Perl diff --git a/.github/workflows/yugabyte.yml b/.github/workflows/yugabyte.yml index 9524be9c2..34c886e38 100644 --- a/.github/workflows/yugabyte.yml +++ b/.github/workflows/yugabyte.yml @@ -34,7 +34,7 @@ jobs: uses: jameshartig/yugabyte-db-action@master with: yb_image_tag: "${{ matrix.tag }}" - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Setup Perl id: perl uses: shogo82148/actions-setup-perl@v1 diff --git a/.gitignore b/.gitignore index 8033e00ee..a3efe757b 100644 --- a/.gitignore +++ b/.gitignore @@ -13,7 +13,8 @@ /_rpmbuild /target .build/ -*.mo .al /latest_changes.md /local/ +/LocaleData/ +/lib/LocaleData/ diff --git a/Changes b/Changes index e2f29ade8..f45ae341f 100644 --- a/Changes +++ b/Changes @@ -25,6 +25,10 @@ Revision history for Perl extension App::Sqitch - Tests now require Test::Warn 0.31 or later, as newline handling issues cause test failures in earlier versions. Thanks to Slaven Rezić for the test reports and identifying the issue. + - Updated the locale configuration to fix issues in more recent versions + of Perl, and added tests to ensure that the sqitch CLI executes and + properly emits localized messages (except on Windows, where the language + codes are incompatible). 1.4.0 2023-08-01T23:37:30Z - Fixed Snowflake warehouse and role setup to properly quote identifiers diff --git a/bin/sqitch b/bin/sqitch index e14a01115..89c77f22d 100755 --- a/bin/sqitch +++ b/bin/sqitch @@ -1,15 +1,6 @@ #!perl -w -CAS # VERSION -use POSIX qw(setlocale); -BEGIN { - if ($^O eq 'MSWin32') { - require Win32::Locale; - setlocale POSIX::LC_ALL, Win32::Locale::get_locale(); - } else { - setlocale POSIX::LC_ALL, ''; - } -} +use locale; use App::Sqitch; - exit App::Sqitch->go; diff --git a/dist.ini b/dist.ini index 262efdd65..890ef97c2 100644 --- a/dist.ini +++ b/dist.ini @@ -2,7 +2,7 @@ name = App-Sqitch license = MIT copyright_holder = "iovation Inc., David E. Wheeler" copyright_year = 2012-2023 -version = v1.4.1-dev +version = v1.4.1 [GatherDir] exclude_filename = dist/cpanfile diff --git a/dist/cpanfile b/dist/cpanfile index 4e19f0173..4558297b4 100644 --- a/dist/cpanfile +++ b/dist/cpanfile @@ -1,4 +1,4 @@ -# This file is generated by Dist::Zilla::Plugin::CPANFile v6.030 +# This file is generated by Dist::Zilla::Plugin::CPANFile v6.031 # Do not edit this file directly. To change prereqs, edit the `dist.ini` file. requires "Algorithm::Backoff::Exponential" => "0.006"; @@ -54,6 +54,7 @@ requires "URI::QueryParam" => "0"; requires "URI::db" => "0.20"; requires "User::pwent" => "0"; requires "constant" => "0"; +requires "locale" => "0"; requires "namespace::autoclean" => "0.16"; requires "overload" => "0"; requires "parent" => "0"; diff --git a/lib/App/Sqitch/Engine.pm b/lib/App/Sqitch/Engine.pm index 0c04a316c..ea408dbf4 100644 --- a/lib/App/Sqitch/Engine.pm +++ b/lib/App/Sqitch/Engine.pm @@ -313,11 +313,10 @@ sub revert { hurl revert => __('Missing required parameter $prompt_default') unless defined $prompt_default; } else { - warnings::warnif( - "deprecated", - "Engine::revert() requires the `prompt` and `prompt_default` arguments.\n" - . 'Omitting them will become fatal in a future release.', - ); + warnings::warnif(deprecated => join ("\n", + "Engine::revert() requires the `prompt` and `prompt_default` arguments.", + 'Omitting them will become fatal in a future release.', + )); $prompt = !($self->no_prompt // 0); $prompt_default = $self->prompt_accept // 1; diff --git a/po/de_DE.po b/po/de_DE.po index 6eceb8bc5..b561564c6 100644 --- a/po/de_DE.po +++ b/po/de_DE.po @@ -10,7 +10,7 @@ msgstr "" "POT-Creation-Date: 2023-07-30 20:02-0400\n" "PO-Revision-Date: 2012-08-31 17:15-0700\n" "Last-Translator: Thomas Iguchi \n" -"Language-Team: German \n" +"Language-Team: Sqitch Hackers \n" "Language: de\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" diff --git a/po/fr_FR.po b/po/fr_FR.po index dc2502eac..283ceb1b9 100644 --- a/po/fr_FR.po +++ b/po/fr_FR.po @@ -10,7 +10,7 @@ msgstr "" "POT-Creation-Date: 2023-07-30 20:02-0400\n" "PO-Revision-Date: 2012-10-12 11:28-0700\n" "Last-Translator: Arnaud Assad \n" -"Language-Team: French \n" +"Language-Team: Sqitch Hackers \n" "Language: fr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" diff --git a/po/it_IT.po b/po/it_IT.po index 6b716b1c4..14a586a3f 100644 --- a/po/it_IT.po +++ b/po/it_IT.po @@ -10,7 +10,7 @@ msgstr "" "POT-Creation-Date: 2023-07-30 20:02-0400\n" "PO-Revision-Date: 2017-10-12 10:30+0200\n" "Last-Translator: Luca Ferrari \n" -"Language-Team: Italian \n" +"Language-Team: Sqitch Hackers \n" "Language: it_IT\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" diff --git a/t/sqitch b/t/sqitch index 350e7fc0a..cf9171dca 100755 --- a/t/sqitch +++ b/t/sqitch @@ -1,14 +1,6 @@ #!/usr/bin/env perl -CAS -use POSIX qw(setlocale); -BEGIN { - if ($^O eq 'MSWin32') { - require Win32::Locale; - setlocale POSIX::LC_ALL, Win32::Locale::get_locale(); - } else { - setlocale POSIX::LC_ALL, ''; - } -} +use locale; use FindBin; use lib "$FindBin::Bin/../lib"; use App::Sqitch; diff --git a/xt/locale/LocaleData/de_DE/LC_MESSAGES/App-Sqitch.mo b/xt/locale/LocaleData/de_DE/LC_MESSAGES/App-Sqitch.mo new file mode 100644 index 000000000..3c1b7c0eb Binary files /dev/null and b/xt/locale/LocaleData/de_DE/LC_MESSAGES/App-Sqitch.mo differ diff --git a/xt/locale/LocaleData/fr_FR/LC_MESSAGES/App-Sqitch.mo b/xt/locale/LocaleData/fr_FR/LC_MESSAGES/App-Sqitch.mo new file mode 100644 index 000000000..5ff3b87c6 Binary files /dev/null and b/xt/locale/LocaleData/fr_FR/LC_MESSAGES/App-Sqitch.mo differ diff --git a/xt/locale/LocaleData/it_IT/LC_MESSAGES/App-Sqitch.mo b/xt/locale/LocaleData/it_IT/LC_MESSAGES/App-Sqitch.mo new file mode 100644 index 000000000..bbb9aa099 Binary files /dev/null and b/xt/locale/LocaleData/it_IT/LC_MESSAGES/App-Sqitch.mo differ diff --git a/xt/locale/README.md b/xt/locale/README.md new file mode 100644 index 000000000..bc5900eb5 --- /dev/null +++ b/xt/locale/README.md @@ -0,0 +1,43 @@ +Sqitch Locale Test +================== + +This directory contains the files necessary to test the Sqitch CLI to ensure it +properly detects locale settings and emits translated messages. The +[`po` directory here](./po/), unlike the canonical translations in the root `po` +directory, contains only a few languages translating a single message: + +``` +"{command}" is not a valid command +``` + +The `LocaleData` directory contains the compiled forms of these dictionaries, +and unlike the main dictionaries, these are committed to the repository. This +allows the [OS](.github/workflows/os.yml) and [Perl](.github/workflows/os.yml) +workflows to run without the overhead of compiling them (a PITA since `gettext` +is hard to get on Windows and Dist::Zilla supports only more recent versions of +Perl). If the messages need to change, recompile the dictionaries with these +commands: + +```sh +cpanm Dist::Zilla --notest +dzil authordeps --missing | cpanm --notest +dzil msg-compile -d xt/locale xt/locale/po/*.po +``` + +For errors where it can't find `msgformat` or `gettext`, be sure that [gettext] +is installed (readily available via `apt-get`, `yum`, or `brew`). + +Now run the test, which validates the output from [`bin/sqitch`](bin/sqitch): + +```sh +prove -lv xt/locale/test-cli.t +``` + +If tests fail, be sure each of the locales is installed on your system. +Apt-based systems, for example, require the relevant language packs: + +```sh +sudo apt-get install -qq language-pack-fr language-pack-en language-pack-de language-pack-it +``` + + [gettext]: https://www.gnu.org/software/gettext/ diff --git a/xt/locale/po/de_DE.po b/xt/locale/po/de_DE.po new file mode 100644 index 000000000..1f1cabee0 --- /dev/null +++ b/xt/locale/po/de_DE.po @@ -0,0 +1,12 @@ +msgid "" +msgstr "" +"Language: de\n" +"Project-Id-Version: Sqitch 1.4.1\n" +"PO-Revision-Date: 22024-01-06T21:10:06Z\n" +"Last-Translator: Sqitch Hackers \n" +"Language-Team: Sqitch Hackers \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +msgid "\"{command}\" is not a valid command" +msgstr "\"{command}\" ist ein ungültiger Befehl" diff --git a/xt/locale/po/fr_FR.po b/xt/locale/po/fr_FR.po new file mode 100644 index 000000000..17df56188 --- /dev/null +++ b/xt/locale/po/fr_FR.po @@ -0,0 +1,12 @@ +msgid "" +msgstr "" +"Language: fr\n" +"Project-Id-Version: Sqitch 1.4.1\n" +"PO-Revision-Date: 22024-01-06T21:10:06Z\n" +"Last-Translator: Sqitch Hackers \n" +"Language-Team: Sqitch Hackers \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +msgid "\"{command}\" is not a valid command" +msgstr "\"{command}\" n'est pas une commande valide" diff --git a/xt/locale/po/it_IT.po b/xt/locale/po/it_IT.po new file mode 100644 index 000000000..0edd5753c --- /dev/null +++ b/xt/locale/po/it_IT.po @@ -0,0 +1,12 @@ +msgid "" +msgstr "" +"Language: it\n" +"Project-Id-Version: Sqitch 1.4.1\n" +"PO-Revision-Date: 22024-01-06T21:10:06Z\n" +"Last-Translator: Sqitch Hackers \n" +"Language-Team: Sqitch Hackers \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +msgid "\"{command}\" is not a valid command" +msgstr "\"{command}\" non è un comando valido" diff --git a/xt/locale/test-cli.t b/xt/locale/test-cli.t new file mode 100644 index 000000000..ec666b2f8 --- /dev/null +++ b/xt/locale/test-cli.t @@ -0,0 +1,57 @@ +#!/usr/bin/perl -w + +use strict; +use warnings; +use Test::More tests => 4; +use File::Spec; +use Capture::Tiny qw(:all); + +# Requires xt/locale/LocaleData; see xt/locale/README.md for details. +my @cli = (qw(-Ilib -CAS -Ixt/locale), File::Spec->catfile(qw(bin sqitch))); + +# Windows has its own locale names for some reason. +# https://stackoverflow.com/q/77771097/79202 +my %lang_for = ( + "en_US" => 'English_United States.1252', + "fr_FR" => 'French_France.1252', + "de_DE" => 'German_Germany.1252', + "it_IT" => 'Italian_Italy.1252', +); + +# Other supported OSes just use the code name. +if ($^O ne 'MSWin32') { + $lang_for{$_} = "$_.UTF-8" for keys %lang_for; +} + +# Each locale must be installed on the local system. Adding a new lang? Also add +# the relevant language-pack-XX package to os.yml and perl.yml. +for my $tc ( + { lang => 'en_US', err => q{"nonesuch" is not a valid command} }, + { lang => 'fr_FR', err => q{"nonesuch" n'est pas une commande valide} }, + { lang => 'de_DE', err => q{"nonesuch" ist ein ungültiger Befehl} }, + { lang => 'it_IT', err => q{"nonesuch" non è un comando valido} }, +) { + subtest $tc->{lang} || 'default' => sub { + local $ENV{LC_ALL} = $lang_for{$tc->{lang}}; + + # Test successful run. + my ($stdout, $stderr, $exit) = capture { system $^X, @cli, 'help' }; + is $exit >> 8, 0, 'Should have exited normally'; + like $stdout, qr/\AUsage\b/, 'Should have usage statement in STDOUT'; + is $stderr, '', 'Should have no STDERR'; + + # Test localized error. + ($stdout, $stderr, $exit) = capture { system $^X, @cli, 'nonesuch' }; + is $exit >> 8, 2, 'Should have exit val 2'; + is $stdout, '', 'Should have no STDOUT'; + TODO: { + # The Windows locales don't translate into the language codes + # recognized by Locale::TextDomain/gettext. Not at all sure how to + # fix this. Some relevant notes in the FAQ: + # https://metacpan.org/dist/libintl-perl/view/lib/Locale/libintlFAQ.pod#How-do-I-switch-languages-or-force-a-certain-language-independently-from-user-settings-read-from-the-environment? + local $TODO = $^O eq 'MSWin32' ? 'localization fails on Windows' : ''; + like $stderr, qr/\A\Q$tc->{err}/, + 'Should have localized error message in STDERR'; + } + }; +}