diff --git a/.github/ubuntu/all-apt-prereqs.sh b/.github/ubuntu/all-apt-prereqs.sh index cc6b253fd..4c8435e15 100755 --- a/.github/ubuntu/all-apt-prereqs.sh +++ b/.github/ubuntu/all-apt-prereqs.sh @@ -1,6 +1,7 @@ set -e sudo apt-get update -qq +sudo apt-get remove -qq mysql-common # https://github.com/actions/virtual-environments/issues/5067#issuecomment-1038752575 sudo env DEBIAN_FRONTEND=noninteractive apt-get install -qq \ libicu-dev gettext aspell-en software-properties-common \ curl unixodbc-dev odbcinst unixodbc \ diff --git a/.github/ubuntu/exasol.sh b/.github/ubuntu/exasol.sh index 97170449b..2ca0d2d67 100755 --- a/.github/ubuntu/exasol.sh +++ b/.github/ubuntu/exasol.sh @@ -2,9 +2,6 @@ set -e -version=${1:-7} -echo $version - # Download dependencies. if [ -z "$SKIP_DEPENDS" ]; then sudo apt-get update -qq @@ -17,17 +14,10 @@ mkdir -p /opt/exasol # Download and unpack Exasol ODBC Driver & EXAplus. # https://www.exasol.com/portal/display/DOWNLOAD/ -if [[ "$version" =~ ^6 ]]; then - curl -sSLO https://www.exasol.com/support/secure/attachment/111075/EXASOL_ODBC-6.2.9.tar.gz - curl -sSLO https://www.exasol.com/support/secure/attachment/111057/EXAplus-6.2.9.tar.gz - sudo tar -xzf EXASOL_ODBC-6.2.9.tar.gz -C /opt/exasol --strip-components 1 - sudo tar -xzf EXAplus-6.2.9.tar.gz -C /opt/exasol --strip-components 1 -else - curl -sSLO https://www.exasol.com/support/secure/attachment/175398/EXASOL_ODBC-7.1.3.tar.gz - curl -sSLO https://www.exasol.com/support/secure/attachment/175394/EXAplus-7.1.3.tar.gz - sudo tar -xzf EXASOL_ODBC-7.1.3.tar.gz -C /opt/exasol --strip-components 1 - sudo tar -xzf EXAplus-7.1.3.tar.gz -C /opt/exasol --strip-components 1 -fi +curl -sSLO https://www.exasol.com/support/secure/attachment/186326/EXASOL_ODBC-7.1.5.tar.gz +curl -sSLO https://www.exasol.com/support/secure/attachment/179176/EXAplus-7.1.4.tar.gz +sudo tar -xzf EXASOL_ODBC-7.1.5.tar.gz -C /opt/exasol --strip-components 1 +sudo tar -xzf EXAplus-7.1.4.tar.gz -C /opt/exasol --strip-components 1 # Add to the path. if [[ ! -z "$GITHUB_PATH" ]]; then diff --git a/.github/ubuntu/mysql.sh b/.github/ubuntu/mysql.sh index 5e378554a..e25180b79 100755 --- a/.github/ubuntu/mysql.sh +++ b/.github/ubuntu/mysql.sh @@ -5,5 +5,6 @@ set -e # Download dependencies. if [ -z "$SKIP_DEPENDS" ]; then sudo apt-get update -qq + sudo apt-get remove -qq mysql-common # https://github.com/actions/virtual-environments/issues/5067#issuecomment-1038752575 sudo env DEBIAN_FRONTEND=noninteractive apt-get install -qq mysql-client default-libmysqlclient-dev fi diff --git a/.github/workflows/cockroach.yml b/.github/workflows/cockroach.yml new file mode 100644 index 000000000..4ef056f9b --- /dev/null +++ b/.github/workflows/cockroach.yml @@ -0,0 +1,37 @@ +# This workflow tests Sqitch's Cockroach engine on all supported versions of +# Postgres. It runs for pushes and pull requests on the `main`, `develop`, +# `**cockroach**`, and `**engine**` branches. +name: ๐Ÿชณ Cockroach +on: + push: + branches: [main, develop, "**engine**", "**cockroach**" ] + pull_request: + branches: [main, develop, "**engine**", "**cockroach**" ] +jobs: + Cockroach: + strategy: + matrix: + version: ['22.1', '21.2'] + name: ๐Ÿชณ Cockroach ${{ matrix.version }} + runs-on: ubuntu-latest + steps: + - name: Start CockroachDB + run: docker run -d -p 26257:26257 cockroachdb/cockroach:latest-v${{ matrix.version }} start-single-node --insecure + - uses: actions/checkout@v2 + - name: Setup Perl + id: perl + uses: shogo82148/actions-setup-perl@v1 + with: { perl-version: latest } + - name: Cache CPAN Modules + uses: actions/cache@v2 + with: + path: local + key: perl-${{ steps.perl.outputs.perl-hash }} + - 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 DBD::Pg + - name: prove + env: + PERL5LIB: "${{ github.workspace }}/local/lib/perl5" + LIVE_COCKROACH_REQUIRED: true + SQITCH_TEST_COCKROACH_URI: db:cockroach://root@localhost:26257/ + run: prove -lvr t/cockroach.t diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index f1416b372..7a68ef0b9 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -1,12 +1,13 @@ # This workflow creates the services and installs the clients in order to run # coverage tests. Each engine must be accessible for a complete coverage report. -# It runs for pushes and pull requests on the main and develop branches. +# It runs for pushes and pull requests on the `main`, `develop`, and `**cover**` +# branches. name: ๐Ÿ“ˆ Coverage on: push: - branches: [main, develop] + branches: [main, develop, "**cover**"] pull_request: - branches: [main, develop] + branches: [main, develop, "**cover**"] jobs: Snowflake: name: ๐Ÿ“ˆ Coverage @@ -43,6 +44,8 @@ jobs: image: vertica/vertica-ce:latest ports: [ 5433 ] steps: + - name: Start CockroachDB + run: docker run -d -p 26257:26257 cockroachdb/cockroach:latest start-single-node --insecure - uses: actions/checkout@v2 - name: Setup Clients env: @@ -90,6 +93,8 @@ jobs: LIVE_SQLITE_REQUIRED: true LIVE_VERTICA_REQUIRED: true SQITCH_TEST_VSQL_URI: db:vertica://dbadmin@localhost:${{ job.services.vertica.ports[5433] }}/VMart?Driver=Vertica + LIVE_COCKROACH_REQUIRED: true + SQITCH_TEST_COCKROACH_URI: db:cockroach://root@localhost:26257/ run: prove -lrj4 t - name: Report Coverage env: diff --git a/.github/workflows/exasol.yml b/.github/workflows/exasol.yml index bc752b74a..da0cedb6e 100644 --- a/.github/workflows/exasol.yml +++ b/.github/workflows/exasol.yml @@ -11,23 +11,18 @@ jobs: Exasol: strategy: matrix: - include: - - { version: '7.1', image: 'latest-7.1', port: 8563 } - - { version: '7.0', image: 'latest-7.0', port: 8563 } - # - { version: '6.2', image: 'latest-6.2', port: 8888 } - # - { version: '6.1', image: 'latest-6.1', port: 8888 } - # - { version: '6.0', image: '6.0.16-d1', port: 8888 } - name: โ˜€๏ธ Exasol ${{ matrix.version }} + exasol: ['7.1', '7.0'] + name: โ˜€๏ธ Exasol ${{ matrix.exasol }} runs-on: ubuntu-latest services: exasol: - image: exasol/docker-db:${{ matrix.image }} - ports: [ "${{ matrix.port }}" ] + image: exasol/docker-db:latest-${{ matrix.exasol }} + ports: [ 8563 ] options: --privileged steps: - uses: actions/checkout@v2 - name: Setup Clients - run: .github/ubuntu/exasol.sh ${{ matrix.version }} + run: .github/ubuntu/exasol.sh - name: Setup Perl id: perl uses: shogo82148/actions-setup-perl@v1 @@ -43,5 +38,5 @@ jobs: env: PERL5LIB: "${{ github.workspace }}/local/lib/perl5" LIVE_EXASOL_REQUIRED: true - SQITCH_TEST_EXASOL_URI: db:exasol://sys:exasol@127.0.0.1:${{ job.services.exasol.ports[matrix.port] }}/?Driver=Exasol;SSLCertificate=SSL_VERIFY_NONE + SQITCH_TEST_EXASOL_URI: db:exasol://sys:exasol@127.0.0.1:${{ job.services.exasol.ports[8563] }}/?Driver=Exasol;SSLCertificate=SSL_VERIFY_NONE run: prove -lvr t/exasol.t diff --git a/.github/workflows/os.yml b/.github/workflows/os.yml index 35db39384..cdefac144 100644 --- a/.github/workflows/os.yml +++ b/.github/workflows/os.yml @@ -3,7 +3,10 @@ # the latest versions of Ubuntu, macOS, and Windows. Think of it as a quick # check for working branches. name: ๐Ÿ’ฟ OS -on: [push, pull_request] +on: + push: + branches: ['*'] + pull_request: jobs: OS: strategy: diff --git a/.github/workflows/perl.yml b/.github/workflows/perl.yml index 6467903b0..1b40fa15b 100644 --- a/.github/workflows/perl.yml +++ b/.github/workflows/perl.yml @@ -12,7 +12,7 @@ jobs: strategy: matrix: os: [[๐Ÿง, ubuntu], [๐ŸŽ, macos], [๐ŸชŸ, windows]] - perl: [ '5.34', '5.32', '5.30', '5.28', '5.26', '5.24', '5.22', '5.20', '5.18', '5.16', '5.14', '5.12' ] + perl: [ '5.36', '5.34', '5.32', '5.30', '5.28', '5.26', '5.24', '5.22', '5.20', '5.18', '5.16', '5.14', '5.12' ] exclude: - { os: [๐ŸชŸ, windows], perl: '5.12' } # https://github.com/shogo82148/actions-setup-perl/issues/876 - { os: [๐ŸชŸ, windows], perl: '5.14' } # https://github.com/shogo82148/actions-setup-perl/issues/881 diff --git a/.github/workflows/pg.yml b/.github/workflows/pg.yml index d9bff994a..b14cb62c8 100644 --- a/.github/workflows/pg.yml +++ b/.github/workflows/pg.yml @@ -1,12 +1,12 @@ # This workflow tests Sqitch's PostgreSQL engine on all supported versions of # Postgres. It runs for pushes and pull requests on the `main`, `develop`, -# `**postgres**`, and `**engine**` branches. +# `**postgres**`, `**yugabyte**`, and `**engine**` branches. name: ๐Ÿ˜ Postgres on: push: - branches: [main, develop, "**engine**", "**postgres**" ] + branches: [main, develop, "**engine**", "**postgres**", "**yugabyte**" ] pull_request: - branches: [main, develop, "**engine**", "**postgres**" ] + branches: [main, develop, "**engine**", "**postgres**", "**yugabyte**" ] jobs: Postgres: strategy: diff --git a/.github/workflows/yugabyte.yml b/.github/workflows/yugabyte.yml new file mode 100644 index 000000000..e1c75f4c4 --- /dev/null +++ b/.github/workflows/yugabyte.yml @@ -0,0 +1,44 @@ +# This workflow tests Sqitch's PostgreSQL engine on all supported versions of +# YugabyteDB. It runs for pushes and pull requests on the `main`, `develop`, +# `**postgres**`, `**yugabyte**`, and `**engine**` branches. +name: ๐Ÿ’ซ Yugabyte +on: + push: + branches: [main, develop, "**engine**", "**postgres**", "**yugabyte**" ] + pull_request: + branches: [main, develop, "**engine**", "**postgres**", "**yugabyte**" ] +jobs: + Yugabyte: + strategy: + matrix: + include: + - { version: '2.13', tag: 2.13.2.0-b135 } + - { version: '2.12', tag: 2.12.5.0-b24 } + - { version: '2.8', tag: 2.8.6.0-b12 } + - { version: '2.6', tag: 2.6.18.0-b3 } + name: ๐Ÿ’ซ Yugabyte ${{ matrix.version }} + runs-on: ubuntu-latest + steps: + - name: Setup YugabyteDB cluster + id: yugabyte + uses: yugabyte/yugabyte-db-action@master + with: + yb_image_tag: "${{ matrix.tag }}" + - uses: actions/checkout@v2 + - name: Setup Perl + id: perl + uses: shogo82148/actions-setup-perl@v1 + with: { perl-version: latest } + - name: Cache CPAN Modules + uses: actions/cache@v2 + with: + path: local + key: perl-${{ steps.perl.outputs.perl-hash }} + - 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 DBD::Pg + - name: prove + env: + PERL5LIB: "${{ github.workspace }}/local/lib/perl5" + LIVE_PG_REQUIRED: true + SQITCH_TEST_PG_URI: db:pg://yugabyte@localhost:${{ steps.yugabyte.outputs.ysql_port }}/ + run: prove -lvr t/pg.t diff --git a/Changes b/Changes index 6bb817968..996d70d48 100644 --- a/Changes +++ b/Changes @@ -1,5 +1,33 @@ Revision history for Perl extension App::Sqitch +1.3.0 2022-08-12T22:09:13Z + - Fixed an issue when testing Firebird on a host with Firebird installed + but no `isql`, and when using a local Firebird (e.g., the Engine12 + provider), which allows only one connection at a time. Thanks to Slaven + Reziฤ‡ for the the reproducible configuration (#597). + - Tweaked the Postgres engine to support Yugabyte. The only unsupported + features are explicit locks, so users need to manually ensure that only + one instance of Sqitch is updating the cluster at a time. + - Added support for CockroachDB. Almost exactly the same as for Postgres, + so the new App::Sqitch::Engine::cockroach class extends + App::Sqitch::Engine::pg to make a few changes. The SQL files with + the registry DDL varies in a few ways, so they're separate. + - Now require URI::db v0.20 for Cockroach and Yugabyte URI support. + - Dropped support for MySQL 5.0. + - Added explicit sorting for aggregated lists (such as the tags associated + with a commit) to the MySQL, Exasol, Snowflake, and Postgres (8.4 and + higher) engines. + - Fixed slow deploys on MariaDB thanks to fractional timestamp support + added in 5.03.05. Thanks to @rbrigot for the PR (#658)! + - Fixed a bug where destination locking failed on the first deploy to + MySQL. Bug introduced along with destination locking in v1.2.0. + Thanks Tom Bloor the report and to Alberto Simรตes for the help + replicating the issue (#601). + - Removed the `sqitch engine update-config` action, originally added for + compatibility reasons in 2014, and the prompt to use it was removed as + of 0.9999 in 2019. + - Fixed a warning when searching for the Firebird client on Windows. + 1.2.1 2021-12-05T19:59:45Z - Updated all the live engine tests, aside from Oracle, to test with unique registry names, so as to avoid conflicts when multiple diff --git a/LICENSE.md b/LICENSE.md index 1b77ca6b3..196d15ce6 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2012-2021 iovation, Inc., David E. Wheeler +Copyright (c) 2012-2022 iovation, Inc., David E. Wheeler Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/README.md b/README.md index 0538b9527..4980916c4 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,27 @@ -App/Sqitch version v1.2.1 +App/Sqitch version v1.3.0 ========================= -| Release | Coverage | Database || -|-------------------|-------------------|-------------------|-------------------| -| [![CPAN]][๐Ÿ“š] | [![OSes]][๐Ÿ’ฟ] | [![Exasol]][โ˜€๏ธ] | [![Oracle]][๐Ÿ”ฎ] | -| [![Docker]][๐Ÿณ] | [![Perl]][๐Ÿง…] | [![Firebird]][๐Ÿ”ฅ] | [![Snowflake]][โ„๏ธ] | -| [![Homebrew]][๐Ÿบ] | [![Coverage]][๐Ÿ“ˆ] | [![MySQL]][๐Ÿฌ] | [![SQLite]][๐Ÿ’ก] | -| [![Debian]][๐Ÿฅ] | | [![Postgres]][๐Ÿ˜] | [![Vertica]][๐Ÿ”บ] | - -[Sqitch] is a database change management application. It currently supports -PostgreSQL 8.4+, SQLite 3.7.11+, MySQL 5.0+, Oracle 10g+, Firebird 2.0+, Vertica -6.0+, Exasol 6.0+ and Snowflake. +| Release | Coverage | Database || +|-------------------|-------------------|-------------------|--------------------| +| [![CPAN]][๐Ÿ“š] | [![OSes]][๐Ÿ’ฟ] | [![Exasol]][โ˜€๏ธ] | [![Oracle]][๐Ÿ”ฎ] | +| [![Docker]][๐Ÿณ] | [![Perl]][๐Ÿง…] | [![Firebird]][๐Ÿ”ฅ] | [![Snowflake]][โ„๏ธ] | +| [![Homebrew]][๐Ÿบ] | [![Coverage]][๐Ÿ“ˆ] | [![MySQL]][๐Ÿฌ] | [![SQLite]][๐Ÿ’ก] | +| [![Debian]][๐Ÿฅ] | | [![Postgres]][๐Ÿ˜] | [![Vertica]][๐Ÿ”บ] | +| | | [![Yugabyte]][๐Ÿ’ซ] | [![Cockroach]][๐Ÿชณ] | + +[Sqitch] is a database change management application. It currently supports: + +* [PostgreSQL] 8.4+ +* [YugabyteDB] 2.6+ +* [CockroachDB] 21+ +* [SQLite][lite] 3.7.11+ +* [MySQL][my] 5.1+ +* [MariaDB] 10.0+ +* [Oracle][orcl] 10g+, +* [Firebird][bird] 2.0+ +* [Vertica][vert] 6.0+ +* [Exasol][exa] 6.0+ +* [Snowflake][flake] What makes it different from your typical migration approaches? A few things: @@ -24,7 +35,7 @@ What makes it different from your typical migration approaches? A few things: Changes are implemented as scripts native to your selected database engine. Writing a [PostgreSQL] application? Write SQL scripts for [`psql`]. Writing - an [Oracle]-backed app? Write SQL scripts for [SQL\*Plus]. + an [Oracle][orcl]-backed app? Write SQL scripts for [SQL\*Plus]. * Dependency resolution @@ -34,7 +45,7 @@ What makes it different from your typical migration approaches? A few things: * Deployment integrity - Sqitch manages changes and dependencies via a plan file, and employs a + Sqitch manages changes and dependencies via a plan file, employing a [Merkle tree] pattern similar to [Git][gitmerkle] and [Blockchain] to ensure deployment integrity. As such, there is no need to number your changes, although you can if you want. Sqitch doesn't much care how you name your @@ -44,13 +55,12 @@ What makes it different from your typical migration approaches? A few things: Up until you [tag] and [release] your project, you can modify your change deployment scripts as often as you like. They're not locked in just because - they've been committed to your VCS. This allows you to take an iterative - approach to developing your database schema. Or, better, you can do - test-driven database development. + they've been committed to your VCS. This allows you to take an iterative or + test-driven approach to developing your database schema. Want to learn more? The best place to start is in the tutorials: -* [Introduction to Sqitch on PostgreSQL](lib/sqitchtutorial.pod) +* [Introduction to Sqitch on PostgreSQL, YugabyteDB, and CockroachDB](lib/sqitchtutorial.pod) * [Introduction to Sqitch on SQLite](lib/sqitchtutorial-sqlite.pod) * [Introduction to Sqitch on Oracle](lib/sqitchtutorial-oracle.pod) * [Introduction to Sqitch on MySQL](lib/sqitchtutorial-mysql.pod) @@ -102,7 +112,7 @@ naming the feature: The feature names generally correspond to the supported engines. The currently supported features are: -* `--with postgres`: Support for managing PostgreSQL databases +* `--with postgres`: Support for managing Postgres, Yugabyte, and Cockroach databases * `--with sqlite`: Support for managing SQLite databases * `--with mysql`: Support for managing MySQL databases * `--with firebird`: Support for managing Firebird databases @@ -128,7 +138,7 @@ Linux distributions and Windows, see the [Installation documentation]. License ------- -Copyright (c) 2012-2021 iovation Inc., David E. Wheeler +Copyright (c) 2012-2022 iovation Inc., David E. Wheeler Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -176,13 +186,26 @@ SOFTWARE. [๐Ÿฅ]: https://packages.debian.org/stable/sqitch "Latest version on Debian" [Postgres]: https://github.com/sqitchers/sqitch/actions/workflows/pg.yml/badge.svg [๐Ÿ˜]: https://github.com/sqitchers/sqitch/actions/workflows/pg.yml "Tested with PostgreSQL 9.3โ€“14" + [Yugabyte]: https://github.com/sqitchers/sqitch/actions/workflows/yugabyte.yml/badge.svg + [๐Ÿ’ซ]: https://github.com/sqitchers/sqitch/actions/workflows/yugabyte.yml "Tested with YugabyteDB 2.6โ€“2.13" [Vertica]: https://github.com/sqitchers/sqitch/actions/workflows/vertica.yml/badge.svg [๐Ÿ”บ]: https://github.com/sqitchers/sqitch/actions/workflows/vertica.yml "Tested with Vertica 7.1โ€“11.0" + [Cockroach]: https://github.com/sqitchers/sqitch/actions/workflows/cockroach.yml/badge.svg + [๐Ÿชณ]: https://github.com/sqitchers/sqitch/actions/workflows/cockroach.yml "Tested with CockroachDB v21-22" [Sqitch]: https://sqitch.org/ [PostgreSQL]: https://postgresql.org/ + [YugabyteDB]: https://www.yugabyte.com/yugabytedb/ + [CockroachDB]: https://www.cockroachlabs.com/product/ + [lite]: https://sqlite.org/ + [my]: https://dev.mysql.com/ + [MariaDB]: https://mariadb.org [`psql`]: https://www.postgresql.org/docs/current/static/app-psql.html - [Oracle]: https://www.oracle.com/database/ + [orcl]: https://www.oracle.com/database/ + [bird]: https://www.firebirdsql.org/ + [vert]: https://www.vertica.com/ + [exa]: https://www.exasol.com/ + [flake]: https://www.snowflake.net/ [SQL\*Plus]: https://www.orafaq.com/wiki/SQL*Plus [Merkle tree]: https://en.wikipedia.org/wiki/Merkle_tree "Wikipedia: โ€œMerkle treeโ€" [gitmerkle]: https://stackoverflow.com/a/18589734/ diff --git a/dist.ini b/dist.ini index 58bf33594..56c0b2bcb 100644 --- a/dist.ini +++ b/dist.ini @@ -1,8 +1,8 @@ name = App-Sqitch license = MIT copyright_holder = "iovation Inc., David E. Wheeler" -copyright_year = 2012-2021 -version = v1.2.1 +copyright_year = 2012-2022 +version = v1.3.0 [GatherDir] exclude_filename = dist/cpanfile @@ -46,11 +46,6 @@ bugtracker.web = https://github.com/sqitchers/sqitch/issues/ [Git::Check] allow_dirty = cpanfile -[Git::Commit] -allow_dirty = cpanfile - -[Git::Push] - [AutoPrereqs] skip = ^Win32 skip = ^DBD:: @@ -97,7 +92,7 @@ relation = recommends ;; Below are dependencies for different engines. [OptionalFeature / postgres] --description = Support for managing PostgreSQL databases +-description = Support for managing Postgres, Yugabyte, and Cockroch databases -prompt = 0 DBD::Pg = 2.0 diff --git a/dist/cpanfile b/dist/cpanfile index 8d35ea402..c1831cda5 100644 --- a/dist/cpanfile +++ b/dist/cpanfile @@ -1,4 +1,4 @@ -# This file is generated by Dist::Zilla::Plugin::CPANFile v6.024 +# This file is generated by Dist::Zilla::Plugin::CPANFile v6.025 # Do not edit this file directly. To change prereqs, edit the `dist.ini` file. requires "Algorithm::Backoff::Exponential" => "0.006"; @@ -51,7 +51,7 @@ requires "Type::Utils" => "0"; requires "Types::Standard" => "0"; requires "URI" => "0"; requires "URI::QueryParam" => "0"; -requires "URI::db" => "0.19"; +requires "URI::db" => "0.20"; requires "User::pwent" => "0"; requires "constant" => "0"; requires "if" => "0"; @@ -97,6 +97,7 @@ on 'test' => sub { requires "Test::Deep" => "0"; requires "Test::Dir" => "0"; requires "Test::Exception" => "0"; + requires "Test::Exit" => "0"; requires "Test::File" => "0"; requires "Test::File::Contents" => "0.20"; requires "Test::MockModule" => "0.17"; @@ -138,8 +139,6 @@ on 'develop' => sub { recommends "Dist::Zilla::Plugin::ExecDir" => "0"; recommends "Dist::Zilla::Plugin::GatherDir" => "0"; recommends "Dist::Zilla::Plugin::Git::Check" => "0"; - recommends "Dist::Zilla::Plugin::Git::Commit" => "0"; - recommends "Dist::Zilla::Plugin::Git::Push" => "0"; recommends "Dist::Zilla::Plugin::License" => "0"; recommends "Dist::Zilla::Plugin::LocaleTextDomain" => "0"; recommends "Dist::Zilla::Plugin::Manifest" => "0"; diff --git a/dist/sqitch.spec b/dist/sqitch.spec index 834b09056..b2e579643 100644 --- a/dist/sqitch.spec +++ b/dist/sqitch.spec @@ -1,5 +1,5 @@ Name: sqitch -Version: 1.2.1 +Version: 1.3.0 Release: 1%{?dist} Summary: Sensible database change management License: MIT @@ -65,6 +65,7 @@ BuildRequires: perl(Term::ANSIColor) >= 2.02 BuildRequires: perl(Test::Deep) BuildRequires: perl(Test::Dir) BuildRequires: perl(Test::Exception) +BuildRequires: perl(Test::Exit) BuildRequires: perl(Test::File) BuildRequires: perl(Test::File::Contents) >= 0.20 BuildRequires: perl(Test::MockModule) >= 0.17 @@ -80,7 +81,7 @@ BuildRequires: perl(Type::Tiny::XS) >= 0.010 BuildRequires: perl(Type::Utils) BuildRequires: perl(Types::Standard) BuildRequires: perl(URI) -BuildRequires: perl(URI::db) >= 0.19 +BuildRequires: perl(URI::db) >= 0.20 BuildRequires: perl(User::pwent) BuildRequires: perl(utf8) BuildRequires: perl(warnings) @@ -137,7 +138,7 @@ Requires: perl(Type::Tiny::XS) >= 0.010 Requires: perl(Type::Utils) Requires: perl(Types::Standard) Requires: perl(URI) -Requires: perl(URI::db) >= 0.19 +Requires: perl(URI::db) >= 0.20 Requires: perl(User::pwent) Requires: perl(utf8) Requires: perl(warnings) @@ -183,7 +184,7 @@ rm -rf $RPM_BUILD_ROOT %config %{etcdir}/* %package pg -Summary: Sensible database change management for PostgreSQL +Summary: Sensible database change management for PostgreSQL, YugabyteDB, and CockroachDB Group: Development/Libraries Requires: sqitch >= %{version} Requires: postgresql >= 8.4.0 @@ -237,7 +238,7 @@ package bundles the Sqitch Oracle support. Summary: Sensible database change management for MySQL Group: Development/Libraries Requires: sqitch >= %{version} -Requires: mysql >= 5.0.0 +Requires: mysql >= 5.1.0 Requires: perl(DBI) Requires: perl(DBD::mysql) >= 4.018 Requires: perl(MySQL::Config) @@ -308,6 +309,11 @@ also be installed. # No additional files required. %changelog +* Fri Aug 12 2022 David E. Wheeler 1.3.0-1 +- Add Test::Exit build requirement. +- Upgrade URI::db to v0.20. +- Increased minimal MySQL version to 5.1. + * Sun Dec 5 2021 David E. Wheeler 1.2.1-1 - Upgrade to v1.2.1. diff --git a/etc/tools/upgrade-registry-to-mysql-5.5.0.sql b/etc/tools/upgrade-registry-to-mysql-5.5.0.sql index ce65d09f7..33f4a0146 100644 --- a/etc/tools/upgrade-registry-to-mysql-5.5.0.sql +++ b/etc/tools/upgrade-registry-to-mysql-5.5.0.sql @@ -3,7 +3,7 @@ -- it to emulate CHECK constraints. It will then also be available for use in -- verify scripts, as described in sqitchtutorial-mysql. If you have an -- existing Sqitch registry that was upgraded from an earlier version of MySQL --- to 5.5.0 or highher, you'll need to run this script to update it, like so: +-- to 5.5.0 or higher, you'll need to run this script to update it, like so: -- mysql -u root sqitch --execute "source `sqitch --etc`/tools/upgrade-registry-to-mysql-5.5.0.sql' diff --git a/etc/tools/upgrade-registry-to-mysql-5.6.4.sql b/etc/tools/upgrade-registry-to-mysql-5.6.4.sql index ecc68fea7..d5738618b 100644 --- a/etc/tools/upgrade-registry-to-mysql-5.6.4.sql +++ b/etc/tools/upgrade-registry-to-mysql-5.6.4.sql @@ -1,7 +1,7 @@ -- This script upgrades the Sqitch registry for MySQL 5.6.4 and higher. Sqitch -- expects datetime columns to have a precision on 5.6.4 and higher. If you have -- an existing Sqitch registry that was upgraded from an earlier version of MySQL --- to 5.6.4 or highher, you'll need to run this script to update it, like so: +-- to 5.6.4 or higher, you'll need to run this script to update it, like so: -- mysql -u root sqitch --execute "source `sqitch --etc`/tools/upgrade-registry-to-mysql-5.6.4.sql' diff --git a/inc/Menlo/Sqitch.pm b/inc/Menlo/Sqitch.pm index 8afbd72f1..d4843ed1b 100644 --- a/inc/Menlo/Sqitch.pm +++ b/inc/Menlo/Sqitch.pm @@ -64,9 +64,7 @@ sub remove_build_dependencies { __DATA__ AppConfig Archive-Tar -Archive-Zip CPAN -CPAN-Checksums CPAN-Common-Index CPAN-DistnameInfo CPAN-Meta @@ -82,7 +80,6 @@ Compress-Raw-Lzma Compress-Raw-Zlib Config-AutoConf DBD-CSV -Data-Compare Date-Manip Devel-CheckLib Devel-GlobalDestruction @@ -92,7 +89,6 @@ Digest Digest-MD5 Dist-CheckConflicts Dumpvalue -Expect ExtUtils-CBuilder ExtUtils-Config ExtUtils-Constant @@ -121,11 +117,11 @@ HTTP-Negotiate HTTP-Tiny HTTP-Tinyish IO-Compress +IO-Compress-Brotli IO-Compress-Lzma IO-HTML IO-Socket-IP IO-Socket-SSL -IO-Tty IO-Zlib IPC-Cmd JSON-PP @@ -176,6 +172,7 @@ Text-Balanced Text-CSV_XS Text-Glob Text-Soundex +Thread-Semaphore Tie-File Tie-Handle-Offset TimeDate @@ -185,10 +182,12 @@ XML-DOM XML-Parser XML-RegExp YAML -YAML-LibYAML YAML-Syck bignum inc-latest +lib libwww-perl libxml-perl local-lib +threads +threads-shared diff --git a/inc/Module/Build/Sqitch.pm b/inc/Module/Build/Sqitch.pm index bfe736366..47fc1e8e5 100644 --- a/inc/Module/Build/Sqitch.pm +++ b/inc/Module/Build/Sqitch.pm @@ -13,7 +13,7 @@ use File::Copy (); __PACKAGE__->add_property($_) for qw(etcdir installed_etcdir); # List one more more engines to include in a bundle install. -# --with postgres --with msyql +# --with postgres --with mysql __PACKAGE__->add_property(with => []); # Set dual_life to true to force dual-life modules such as Pod::Simple to be diff --git a/lib/App/Sqitch.pm b/lib/App/Sqitch.pm index 56611beb9..a8d4b6433 100644 --- a/lib/App/Sqitch.pm +++ b/lib/App/Sqitch.pm @@ -264,15 +264,13 @@ sub _parse_core_opts { # Handle version request. if ( delete $opts{version} ) { - require File::Basename; - my $fn = File::Basename::basename($0); - print $fn, ' (', __PACKAGE__, ') ', __PACKAGE__->VERSION, "\n"; + $self->emit( _bn($0), ' (', __PACKAGE__, ') ', __PACKAGE__->VERSION ); exit; } # Handle --etc-path. if ( $opts{etc_path} ) { - say App::Sqitch::Config->class->system_dir; + $self->emit( App::Sqitch::Config->class->system_dir ); exit; } @@ -906,7 +904,7 @@ David E. Wheeler =head1 License -Copyright (c) 2012-2021 iovation Inc., David E. Wheeler +Copyright (c) 2012-2022 iovation Inc., David E. Wheeler Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/lib/App/Sqitch/Command.pm b/lib/App/Sqitch/Command.pm index 8161186e5..ff4ebfab7 100644 --- a/lib/App/Sqitch/Command.pm +++ b/lib/App/Sqitch/Command.pm @@ -8,6 +8,7 @@ use Try::Tiny; use Locale::TextDomain qw(App-Sqitch); use App::Sqitch::X qw(hurl); use Hash::Merge 'merge'; +use File::Path qw(make_path); use Moo; use App::Sqitch::Types qw(Sqitch Target); @@ -239,7 +240,7 @@ sub parse_args { $seen{$target->name}++; } - # Iterate over the argsx to look for changes, engines, plans, or targets. + # Iterate over the args to look for changes, engines, plans, or targets. my %engines = map { $_ => 1 } ENGINES; for my $arg (@{ $p{args} }) { if ( !$p{no_changes} && $target && -e $target->plan_file && $target->plan->contains($arg) ) { @@ -309,6 +310,22 @@ sub parse_args { return (@names, \@targets, $rec{changes}); } +sub _mkpath { + my ( $self, $dir ) = @_; + $self->debug( ' ', __x 'Created {file}', file => $dir ) + if make_path $dir, { error => \my $err }; + + my $diag = shift @{ $err } or return $self; + + my ( $path, $msg ) = %{ $diag }; + hurl $self->command => __x( + 'Error creating {path}: {error}', + path => $path, + error => $msg, + ) if $path; + hurl $self->command => $msg; +} + 1; __END__ @@ -751,7 +768,7 @@ David E. Wheeler =head1 License -Copyright (c) 2012-2021 iovation Inc., David E. Wheeler +Copyright (c) 2012-2022 iovation Inc., David E. Wheeler Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/lib/App/Sqitch/Command/add.pm b/lib/App/Sqitch/Command/add.pm index 5bd08fcfc..703c58939 100644 --- a/lib/App/Sqitch/Command/add.pm +++ b/lib/App/Sqitch/Command/add.pm @@ -10,7 +10,6 @@ use Moo; use App::Sqitch::Types qw(Str Int ArrayRef HashRef Dir Bool Maybe); use Path::Class; use Try::Tiny; -use File::Path qw(make_path); use Clone qw(clone); use List::Util qw(first); use namespace::autoclean; @@ -372,16 +371,7 @@ sub _add { } # Create the directory for the file, if it does not exist. - make_path $file->dir->stringify, { error => \my $err }; - if ( my $diag = shift @{ $err } ) { - my ( $path, $msg ) = %{ $diag }; - hurl add => __x( - 'Error creating {path}: {error}', - path => $path, - error => $msg, - ) if $path; - hurl add => $msg; - } + $self->_mkpath($file->dir->stringify); my $vars = clone { %{ $self->variables }, @@ -542,7 +532,7 @@ David E. Wheeler =head1 License -Copyright (c) 2012-2021 iovation Inc., David E. Wheeler +Copyright (c) 2012-2022 iovation Inc., David E. Wheeler Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/lib/App/Sqitch/Command/bundle.pm b/lib/App/Sqitch/Command/bundle.pm index d51aa77df..aff0966aa 100644 --- a/lib/App/Sqitch/Command/bundle.pm +++ b/lib/App/Sqitch/Command/bundle.pm @@ -129,22 +129,6 @@ sub execute { return $self; } -sub _mkpath { - my ( $self, $dir ) = @_; - $self->debug( ' ', __x 'Created {file}', file => $dir ) - if make_path $dir, { error => \my $err }; - - my $diag = shift @{ $err } or return $self; - - my ( $path, $msg ) = %{ $diag }; - hurl bundle => __x( - 'Error creating {path}: {error}', - path => $path, - error => $msg, - ) if $path; - hurl bundle => $msg; -} - sub _copy_if_modified { my ( $self, $src, $dst ) = @_; @@ -375,7 +359,7 @@ David E. Wheeler =head1 License -Copyright (c) 2012-2021 iovation Inc., David E. Wheeler +Copyright (c) 2012-2022 iovation Inc., David E. Wheeler Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/lib/App/Sqitch/Command/check.pm b/lib/App/Sqitch/Command/check.pm index 7155dcc02..1fbb53399 100644 --- a/lib/App/Sqitch/Command/check.pm +++ b/lib/App/Sqitch/Command/check.pm @@ -176,7 +176,7 @@ Matthieu Foucault =head1 License -Copyright (c) 2012-2021 iovation Inc., David E. Wheeler, Button Inc. +Copyright (c) 2012-2022 iovation Inc., David E. Wheeler, Button Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/lib/App/Sqitch/Command/checkout.pm b/lib/App/Sqitch/Command/checkout.pm index 55726695c..4b3b58111 100644 --- a/lib/App/Sqitch/Command/checkout.pm +++ b/lib/App/Sqitch/Command/checkout.pm @@ -199,7 +199,7 @@ The Sqitch command-line client. =head1 License -Copyright (c) 2012-2021 iovation Inc., David E. Wheeler +Copyright (c) 2012-2022 iovation Inc., David E. Wheeler Copyright (c) 2012-2013 Ronan Dunklau diff --git a/lib/App/Sqitch/Command/config.pm b/lib/App/Sqitch/Command/config.pm index bcfc00468..3f7cb0f46 100644 --- a/lib/App/Sqitch/Command/config.pm +++ b/lib/App/Sqitch/Command/config.pm @@ -642,7 +642,7 @@ David E. Wheeler =head1 License -Copyright (c) 2012-2021 iovation Inc., David E. Wheeler +Copyright (c) 2012-2022 iovation Inc., David E. Wheeler Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/lib/App/Sqitch/Command/deploy.pm b/lib/App/Sqitch/Command/deploy.pm index 2bcd01522..d2f6d840b 100644 --- a/lib/App/Sqitch/Command/deploy.pm +++ b/lib/App/Sqitch/Command/deploy.pm @@ -221,7 +221,7 @@ David E. Wheeler =head1 License -Copyright (c) 2012-2021 iovation Inc., David E. Wheeler +Copyright (c) 2012-2022 iovation Inc., David E. Wheeler Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/lib/App/Sqitch/Command/engine.pm b/lib/App/Sqitch/Command/engine.pm index a28bdea45..630f21a44 100644 --- a/lib/App/Sqitch/Command/engine.pm +++ b/lib/App/Sqitch/Command/engine.pm @@ -234,121 +234,6 @@ sub show { return $self; } -# DEPRECATTION: Added in v0.997 (Oct 2014). As of v0.9999, Sqitch no longer -# notices and warns about core.$engine; most folks should long since have -# updated their configurations. Keeping this method for now, since it might -# still be useful and doesn't add much overhead in general except for the -# compilation of the engine command. But consider removing in the future. -sub update_config { - my $self = shift; - my $sqitch = $self->sqitch; - my $config = $sqitch->config; - - my $local_file = $config->local_file; - for my $file ( - $local_file, - $config->user_file, - $config->system_file, - ) { - $sqitch->emit(__x( 'Loading {file}', file => $file )); - # Hide all other files. Just want to deal with the one. - local $ENV{SQITCH_CONFIG} = '/dev/null/not.conf'; - local $ENV{SQITCH_USER_CONFIG} = '/dev/null/not.user'; - local $ENV{SQITCH_SYSTEM_CONFIG} = '/dev/null/not.sys'; - my $c = App::Sqitch::Config->new; - $c->load_file($file); - my %engines; - for my $ekey (App::Sqitch::Command::ENGINES) { - my $sect = $c->get_section( section => "core.$ekey"); - if (%{ $sect }) { - if (%{ $c->get_section( section => "engine.$ekey") }) { - $sqitch->warn(' - ' . __x( - "Deprecated {section} found in {file}; to remove it, run\n {sqitch} config --file {file} --remove-section {section}", - section => "core.$ekey", - file => $file, - sqitch => $0, - )); - next; - } - # Migrate this one. - $engines{$ekey} = $sect; - } - } - unless (%engines) { - $sqitch->emit(__ ' - No engines to update'); - next; - } - - # Make sure we can write to the file. - unless (-w $file) { - $sqitch->warn(' - ' . __x( - 'Cannot update {file}. Please make it writable', - file => $file, - )); - next; - } - - # Move all of the engines. - for my $ekey (sort keys %engines) { - my $old = $engines{$ekey}; - - my @new; - if ( my $target = delete $old->{target} ) { - # Good, there is already a specific target. - push @new => { - key => "engine.$ekey.target", - value => $target, - }; - # Kill off deprecated variables. - delete $old->{$_} for qw(host port username password db_name); - } elsif ( $file eq $local_file ) { - # Start with a default and migrate deprecated configs. - my $uri = URI::db->new("db:$ekey:"); - for my $spec ( - [host => 'host'], - [port => 'port'], - [username => 'user'], - [password => 'password'], - [db_name => 'dbname'], - ) { - my ($key, $meth) = @{ $spec }; - my $val = delete $old->{$key} or next; - $uri->$meth($val); - } - push @new => { - key => "engine.$ekey.target", - value => $uri->as_string, - }; - } else { - # Just kill off any of the deprecated variables. - delete $old->{$_} for qw(host port username password db_name); - } - - # Copy over the remaining variabls. - push @new => map {{ - key => "engine.$ekey.$_", - value => $old->{$_}, - }} keys %{ $old }; - - # Create the new variables and delete the old section. - $config->group_set( $file, \@new ); - # $c->rename_section( - # from => "core.$ekey", - # filename => $file, - # ); - - $sqitch->emit(' - ' . __x( - "Migrated {old} to {new}; To remove {old}, run\n {sqitch} config --file {file} --remove-section {old}", - old => "core.$ekey", - new => "engine.$ekey", - sqitch => $0, - file => $file, - )); - } - } - return $self; -} - 1; __END__ @@ -410,10 +295,6 @@ Implements the C action. Implements the C action. -=head3 C - -Implements the C action. - =head1 See Also =over @@ -434,7 +315,7 @@ David E. Wheeler =head1 License -Copyright (c) 2012-2021 iovation Inc., David E. Wheeler +Copyright (c) 2012-2022 iovation Inc., David E. Wheeler Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/lib/App/Sqitch/Command/help.pm b/lib/App/Sqitch/Command/help.pm index 1050e7bc2..ba861f40e 100644 --- a/lib/App/Sqitch/Command/help.pm +++ b/lib/App/Sqitch/Command/help.pm @@ -119,7 +119,7 @@ David E. Wheeler =head1 License -Copyright (c) 2012-2021 iovation Inc., David E. Wheeler +Copyright (c) 2012-2022 iovation Inc., David E. Wheeler Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/lib/App/Sqitch/Command/init.pm b/lib/App/Sqitch/Command/init.pm index e6f01ece9..1e5df996b 100644 --- a/lib/App/Sqitch/Command/init.pm +++ b/lib/App/Sqitch/Command/init.pm @@ -268,7 +268,7 @@ David E. Wheeler =head1 License -Copyright (c) 2012-2021 iovation Inc., David E. Wheeler +Copyright (c) 2012-2022 iovation Inc., David E. Wheeler Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/lib/App/Sqitch/Command/log.pm b/lib/App/Sqitch/Command/log.pm index 477eec640..039d9171a 100644 --- a/lib/App/Sqitch/Command/log.pm +++ b/lib/App/Sqitch/Command/log.pm @@ -350,7 +350,7 @@ David E. Wheeler =head1 License -Copyright (c) 2012-2021 iovation Inc., David E. Wheeler +Copyright (c) 2012-2022 iovation Inc., David E. Wheeler Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/lib/App/Sqitch/Command/plan.pm b/lib/App/Sqitch/Command/plan.pm index e61a3e94b..d0d0c9589 100644 --- a/lib/App/Sqitch/Command/plan.pm +++ b/lib/App/Sqitch/Command/plan.pm @@ -331,7 +331,7 @@ David E. Wheeler =head1 License -Copyright (c) 2012-2021 iovation Inc., David E. Wheeler +Copyright (c) 2012-2022 iovation Inc., David E. Wheeler Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/lib/App/Sqitch/Command/rebase.pm b/lib/App/Sqitch/Command/rebase.pm index 4cc06c150..3ac646de3 100644 --- a/lib/App/Sqitch/Command/rebase.pm +++ b/lib/App/Sqitch/Command/rebase.pm @@ -175,7 +175,7 @@ David E. Wheeler =head1 License -Copyright (c) 2012-2021 iovation Inc., David E. Wheeler +Copyright (c) 2012-2022 iovation Inc., David E. Wheeler Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/lib/App/Sqitch/Command/revert.pm b/lib/App/Sqitch/Command/revert.pm index 18e35781d..7c54838f7 100644 --- a/lib/App/Sqitch/Command/revert.pm +++ b/lib/App/Sqitch/Command/revert.pm @@ -241,7 +241,7 @@ David E. Wheeler =head1 License -Copyright (c) 2012-2021 iovation Inc., David E. Wheeler +Copyright (c) 2012-2022 iovation Inc., David E. Wheeler Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/lib/App/Sqitch/Command/rework.pm b/lib/App/Sqitch/Command/rework.pm index 94c08386b..ddaa10596 100644 --- a/lib/App/Sqitch/Command/rework.pm +++ b/lib/App/Sqitch/Command/rework.pm @@ -310,7 +310,7 @@ David E. Wheeler =head1 License -Copyright (c) 2012-2021 iovation Inc., David E. Wheeler +Copyright (c) 2012-2022 iovation Inc., David E. Wheeler Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/lib/App/Sqitch/Command/show.pm b/lib/App/Sqitch/Command/show.pm index 456d85625..2170ae2c7 100644 --- a/lib/App/Sqitch/Command/show.pm +++ b/lib/App/Sqitch/Command/show.pm @@ -181,7 +181,7 @@ David E. Wheeler =head1 License -Copyright (c) 2012-2021 iovation Inc., David E. Wheeler +Copyright (c) 2012-2022 iovation Inc., David E. Wheeler Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/lib/App/Sqitch/Command/status.pm b/lib/App/Sqitch/Command/status.pm index f6d0ca629..2985008a9 100644 --- a/lib/App/Sqitch/Command/status.pm +++ b/lib/App/Sqitch/Command/status.pm @@ -409,7 +409,7 @@ David E. Wheeler =head1 License -Copyright (c) 2012-2021 iovation Inc., David E. Wheeler +Copyright (c) 2012-2022 iovation Inc., David E. Wheeler Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/lib/App/Sqitch/Command/tag.pm b/lib/App/Sqitch/Command/tag.pm index d1e60fb6c..85ea4a15e 100644 --- a/lib/App/Sqitch/Command/tag.pm +++ b/lib/App/Sqitch/Command/tag.pm @@ -183,7 +183,7 @@ David E. Wheeler =head1 License -Copyright (c) 2012-2021 iovation Inc., David E. Wheeler +Copyright (c) 2012-2022 iovation Inc., David E. Wheeler Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/lib/App/Sqitch/Command/target.pm b/lib/App/Sqitch/Command/target.pm index b9140f8f9..0ae91149b 100644 --- a/lib/App/Sqitch/Command/target.pm +++ b/lib/App/Sqitch/Command/target.pm @@ -314,7 +314,7 @@ David E. Wheeler =head1 License -Copyright (c) 2012-2021 iovation Inc., David E. Wheeler +Copyright (c) 2012-2022 iovation Inc., David E. Wheeler Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/lib/App/Sqitch/Command/upgrade.pm b/lib/App/Sqitch/Command/upgrade.pm index e57e3c7fa..e264c64b5 100644 --- a/lib/App/Sqitch/Command/upgrade.pm +++ b/lib/App/Sqitch/Command/upgrade.pm @@ -7,7 +7,6 @@ use utf8; use Moo; use App::Sqitch::Types qw(URI Maybe Str Bool HashRef); use Locale::TextDomain qw(App-Sqitch); -use Type::Utils qw(enum); use App::Sqitch::X qw(hurl); use List::Util qw(first); use namespace::autoclean; @@ -125,7 +124,7 @@ David E. Wheeler =head1 License -Copyright (c) 2012-2021 iovation Inc., David E. Wheeler +Copyright (c) 2012-2022 iovation Inc., David E. Wheeler Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/lib/App/Sqitch/Command/verify.pm b/lib/App/Sqitch/Command/verify.pm index 7b1d070dc..9981bd209 100644 --- a/lib/App/Sqitch/Command/verify.pm +++ b/lib/App/Sqitch/Command/verify.pm @@ -182,7 +182,7 @@ David E. Wheeler =head1 License -Copyright (c) 2012-2021 iovation Inc., David E. Wheeler +Copyright (c) 2012-2022 iovation Inc., David E. Wheeler Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/lib/App/Sqitch/Config.pm b/lib/App/Sqitch/Config.pm index 8e046c99e..56f383813 100644 --- a/lib/App/Sqitch/Config.pm +++ b/lib/App/Sqitch/Config.pm @@ -208,7 +208,7 @@ David E. Wheeler =head1 License -Copyright (c) 2012-2021 iovation Inc., David E. Wheeler +Copyright (c) 2012-2022 iovation Inc., David E. Wheeler Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/lib/App/Sqitch/DateTime.pm b/lib/App/Sqitch/DateTime.pm index 9116b5097..02cbdcba1 100644 --- a/lib/App/Sqitch/DateTime.pm +++ b/lib/App/Sqitch/DateTime.pm @@ -191,7 +191,7 @@ David E. Wheeler =head1 License -Copyright (c) 2012-2021 iovation Inc., David E. Wheeler +Copyright (c) 2012-2022 iovation Inc., David E. Wheeler Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/lib/App/Sqitch/Engine.pm b/lib/App/Sqitch/Engine.pm index c3056f95f..91fe757e9 100644 --- a/lib/App/Sqitch/Engine.pm +++ b/lib/App/Sqitch/Engine.pm @@ -9,7 +9,7 @@ use Locale::TextDomain qw(App-Sqitch); use Path::Class qw(file); use App::Sqitch::X qw(hurl); use List::Util qw(first max); -use URI::db 0.19; +use URI::db 0.20; use App::Sqitch::Types qw(Str Int Num Sqitch Plan Bool HashRef URI Maybe Target); use namespace::autoclean; use constant registry_release => '1.1'; @@ -192,6 +192,7 @@ sub deploy { my $sqitch = $self->sqitch; my $plan = $self->_sync_plan; my $to_index = $plan->count - 1; + my $position = $plan->position; hurl plan => __ 'Nothing to deploy (empty plan)' if $to_index < 0; @@ -202,7 +203,7 @@ sub deploy { ); # Just return if there is nothing to do. - if ($to_index == $plan->position) { + if ($to_index == $position) { $sqitch->info(__x( 'Nothing to deploy (already at "{change}")', change => $to @@ -211,12 +212,12 @@ sub deploy { } } - if ($plan->position == $to_index) { + if ($position == $to_index) { # We are up-to-date. $sqitch->info( __ 'Nothing to deploy (up-to-date)' ); return $self; - } elsif ($plan->position == -1) { + } elsif ($position == -1) { # Initialize or upgrade the database, if necessary. if ($self->initialized) { $self->upgrade_registry; @@ -232,7 +233,7 @@ sub deploy { } else { # Make sure that $to_index is greater than the current point. hurl deploy => __ 'Cannot deploy to an earlier change; use "revert" instead' - if $to_index < $plan->position; + if $to_index < $position; # Upgrade database if it needs it. $self->upgrade_registry; } @@ -262,7 +263,7 @@ sub deploy { $self->max_name_length( max map { length $_->format_name_with_tags - } ($plan->changes)[$plan->position + 1..$to_index] + } ($plan->changes)[$position + 1..$to_index] ); $self->$meth( $plan, $to_index ); @@ -270,11 +271,13 @@ sub deploy { sub revert { my ( $self, $to ) = @_; - $self->lock_destination; + + # Check the registry and, once we know it's there, lock the destination. $self->_check_registry; + $self->lock_destination; + my $sqitch = $self->sqitch; my $plan = $self->plan; - my @changes; if (defined $to) { @@ -896,7 +899,7 @@ sub _deploy_all { push @run => $change; } } catch { - if (my $ident = eval { $_->ident }) { + if (my $ident = try { $_->ident }) { $self->sqitch->vent($_->message) unless $ident eq 'private' } else { $self->sqitch->vent($_); @@ -1068,7 +1071,6 @@ sub _timeout { return 0 if $secs < 0; sleep $secs; } - return 0; } sub try_lock { 1 } @@ -2664,7 +2666,7 @@ David E. Wheeler =head1 License -Copyright (c) 2012-2021 iovation Inc., David E. Wheeler +Copyright (c) 2012-2022 iovation Inc., David E. Wheeler Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/lib/App/Sqitch/Engine/Upgrade/cockroach-1.0.sql b/lib/App/Sqitch/Engine/Upgrade/cockroach-1.0.sql new file mode 100644 index 000000000..36450173a --- /dev/null +++ b/lib/App/Sqitch/Engine/Upgrade/cockroach-1.0.sql @@ -0,0 +1,28 @@ +SET client_min_messages = warning; + +CREATE TABLE :"registry".releases ( + version REAL PRIMARY KEY, + installed_at TIMESTAMPTZ NOT NULL DEFAULT clock_timestamp(), + installer_name TEXT NOT NULL, + installer_email TEXT NOT NULL +); + +COMMENT ON TABLE :"registry".releases IS 'Sqitch registry releases.'; +COMMENT ON COLUMN :"registry".releases.version IS 'Version of the Sqitch registry.'; +COMMENT ON COLUMN :"registry".releases.installed_at IS 'Date the registry release was installed.'; +COMMENT ON COLUMN :"registry".releases.installer_name IS 'Name of the user who installed the registry release.'; +COMMENT ON COLUMN :"registry".releases.installer_email IS 'Email address of the user who installed the registry release.'; + +-- Add the script_hash column to the changes table. Copy change_id for now. +ALTER TABLE :"registry".changes ADD COLUMN script_hash TEXT NULL UNIQUE; +UPDATE :"registry".changes SET script_hash = change_id; +COMMENT ON COLUMN :"registry".changes.script_hash IS 'Deploy script SHA-1 hash.'; + +-- Allow "merge" events. +-- Stricly speaking the `IF EXISTS` should not be required here, but funkyness +-- in Cockroach schema changes require it. +ALTER TABLE :"registry".events DROP CONSTRAINT IF EXISTS events_event_check; +ALTER TABLE :"registry".events ADD CONSTRAINT events_event_check + CHECK (event IN ('deploy', 'revert', 'fail', 'merge')); + +COMMENT ON SCHEMA :"registry" IS 'Sqitch database deployment metadata v1.0.'; diff --git a/lib/App/Sqitch/Engine/Upgrade/cockroach-1.1.sql b/lib/App/Sqitch/Engine/Upgrade/cockroach-1.1.sql new file mode 100644 index 000000000..d08fb37a3 --- /dev/null +++ b/lib/App/Sqitch/Engine/Upgrade/cockroach-1.1.sql @@ -0,0 +1,4 @@ +SET client_min_messages = warning; +DROP INDEX :"registry".changes_script_hash_key CASCADE; +ALTER TABLE :"registry".changes ADD UNIQUE (project, script_hash); +COMMENT ON SCHEMA :"registry" IS 'Sqitch database deployment metadata v1.1.'; diff --git a/lib/App/Sqitch/Engine/Upgrade/pg-1.0.sql b/lib/App/Sqitch/Engine/Upgrade/pg-1.0.sql index fda822166..49f941762 100644 --- a/lib/App/Sqitch/Engine/Upgrade/pg-1.0.sql +++ b/lib/App/Sqitch/Engine/Upgrade/pg-1.0.sql @@ -7,7 +7,7 @@ CREATE TABLE :"registry".releases ( installed_at TIMESTAMPTZ NOT NULL DEFAULT clock_timestamp(), installer_name TEXT NOT NULL, installer_email TEXT NOT NULL -); +):tableopts; COMMENT ON TABLE :"registry".releases IS 'Sqitch registry releases.'; COMMENT ON COLUMN :"registry".releases.version IS 'Version of the Sqitch registry.'; diff --git a/lib/App/Sqitch/Engine/cockroach.pm b/lib/App/Sqitch/Engine/cockroach.pm new file mode 100644 index 000000000..f9afd3560 --- /dev/null +++ b/lib/App/Sqitch/Engine/cockroach.pm @@ -0,0 +1,94 @@ +package App::Sqitch::Engine::cockroach; + +use 5.010; +use Moo; +use namespace::autoclean; + +extends 'App::Sqitch::Engine::pg'; + +# VERSION + +sub key { 'cockroach' } +sub name { 'CockroachDB' } +sub driver { 'DBD::Pg 2.0' } + +sub _ts2char_format { + q{experimental_strftime(%s AT TIME ZONE 'UTC', 'year:%%Y:month:%%m:day:%%d:hour:%%H:minute:%%M:second:%%S:time_zone:UTC')}; +} + +# Override to avoid locking the changes table, as Cockroach does not support +# explicit table locks. +sub begin_work { + my $self = shift; + $self->dbh->begin_work; + return $self; +} + +# Override to return true, as Cockroach does not support advisory locks. +sub wait_lock { + # Cockroach does not support advisory locks. + # https://github.com/cockroachdb/cockroach/issues/13546 + return 1; +} + +sub _no_table_error { + $DBI::state && $DBI::state eq '42P01'; # undefined_table +} + +sub _run_registry_file { + my ($self, $file) = @_; + my $schema = $self->registry; + + $self->_run( + '--file' => $file, + '--set' => "registry=$schema", + ); + + $self->dbh->do('SET search_path = ?', undef, $schema); +} + +1; + +__END__ + +=head1 Name + +App::Sqitch::Engine::cockroach - Sqitch CockroachDB Engine + +=head1 Synopsis + + my $pg = App::Sqitch::Engine->load( engine => 'cockroach' ); + +=head1 Description + +App::Sqitch::Engine::cockroach provides the CockroachDB storage engine for Sqitch. It +supports CockroachDB v21 and higher, and relies on the Postgres toolchain (C +client, L database driver, etc.). + +=head1 Author + +David E. Wheeler + +=head1 License + +Copyright (c) 2012-2022 iovation Inc., David E. Wheeler + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +=cut diff --git a/lib/App/Sqitch/Engine/cockroach.sql b/lib/App/Sqitch/Engine/cockroach.sql new file mode 100644 index 000000000..717bdd26a --- /dev/null +++ b/lib/App/Sqitch/Engine/cockroach.sql @@ -0,0 +1,141 @@ +SET client_min_messages = warning; +CREATE SCHEMA IF NOT EXISTS :"registry"; + +COMMENT ON SCHEMA :"registry" IS 'Sqitch database deployment metadata v1.1.'; + +CREATE TABLE :"registry".releases ( + version REAL PRIMARY KEY, + installed_at TIMESTAMPTZ NOT NULL DEFAULT clock_timestamp(), + installer_name TEXT NOT NULL, + installer_email TEXT NOT NULL +); + +COMMENT ON TABLE :"registry".releases IS 'Sqitch registry releases.'; +COMMENT ON COLUMN :"registry".releases.version IS 'Version of the Sqitch registry.'; +COMMENT ON COLUMN :"registry".releases.installed_at IS 'Date the registry release was installed.'; +COMMENT ON COLUMN :"registry".releases.installer_name IS 'Name of the user who installed the registry release.'; +COMMENT ON COLUMN :"registry".releases.installer_email IS 'Email address of the user who installed the registry release.'; + +CREATE TABLE :"registry".projects ( + project TEXT PRIMARY KEY, + uri TEXT NULL UNIQUE, + created_at TIMESTAMPTZ NOT NULL DEFAULT clock_timestamp(), + creator_name TEXT NOT NULL, + creator_email TEXT NOT NULL +); + +COMMENT ON TABLE :"registry".projects IS 'Sqitch projects deployed to this database.'; +COMMENT ON COLUMN :"registry".projects.project IS 'Unique Name of a project.'; +COMMENT ON COLUMN :"registry".projects.uri IS 'Optional project URI'; +COMMENT ON COLUMN :"registry".projects.created_at IS 'Date the project was added to the database.'; +COMMENT ON COLUMN :"registry".projects.creator_name IS 'Name of the user who added the project.'; +COMMENT ON COLUMN :"registry".projects.creator_email IS 'Email address of the user who added the project.'; + +CREATE TABLE :"registry".changes ( + change_id TEXT PRIMARY KEY, + script_hash TEXT NULL, + change TEXT NOT NULL, + project TEXT NOT NULL REFERENCES :"registry".projects(project) ON UPDATE CASCADE, + note TEXT NOT NULL DEFAULT '', + committed_at TIMESTAMPTZ NOT NULL DEFAULT clock_timestamp(), + committer_name TEXT NOT NULL, + committer_email TEXT NOT NULL, + planned_at TIMESTAMPTZ NOT NULL, + planner_name TEXT NOT NULL, + planner_email TEXT NOT NULL, + UNIQUE(project, script_hash) +); + +COMMENT ON TABLE :"registry".changes IS 'Tracks the changes currently deployed to the database.'; +COMMENT ON COLUMN :"registry".changes.change_id IS 'Change primary key.'; +COMMENT ON COLUMN :"registry".changes.script_hash IS 'Deploy script SHA-1 hash.'; +COMMENT ON COLUMN :"registry".changes.change IS 'Name of a deployed change.'; +COMMENT ON COLUMN :"registry".changes.project IS 'Name of the Sqitch project to which the change belongs.'; +COMMENT ON COLUMN :"registry".changes.note IS 'Description of the change.'; +COMMENT ON COLUMN :"registry".changes.committed_at IS 'Date the change was deployed.'; +COMMENT ON COLUMN :"registry".changes.committer_name IS 'Name of the user who deployed the change.'; +COMMENT ON COLUMN :"registry".changes.committer_email IS 'Email address of the user who deployed the change.'; +COMMENT ON COLUMN :"registry".changes.planned_at IS 'Date the change was added to the plan.'; +COMMENT ON COLUMN :"registry".changes.planner_name IS 'Name of the user who planed the change.'; +COMMENT ON COLUMN :"registry".changes.planner_email IS 'Email address of the user who planned the change.'; + +CREATE TABLE :"registry".tags ( + tag_id TEXT PRIMARY KEY, + tag TEXT NOT NULL, + project TEXT NOT NULL REFERENCES :"registry".projects(project) ON UPDATE CASCADE, + change_id TEXT NOT NULL REFERENCES :"registry".changes(change_id) ON UPDATE CASCADE, + note TEXT NOT NULL DEFAULT '', + committed_at TIMESTAMPTZ NOT NULL DEFAULT clock_timestamp(), + committer_name TEXT NOT NULL, + committer_email TEXT NOT NULL, + planned_at TIMESTAMPTZ NOT NULL, + planner_name TEXT NOT NULL, + planner_email TEXT NOT NULL, + UNIQUE(project, tag) +); + +COMMENT ON TABLE :"registry".tags IS 'Tracks the tags currently applied to the database.'; +COMMENT ON COLUMN :"registry".tags.tag_id IS 'Tag primary key.'; +COMMENT ON COLUMN :"registry".tags.tag IS 'Project-unique tag name.'; +COMMENT ON COLUMN :"registry".tags.project IS 'Name of the Sqitch project to which the tag belongs.'; +COMMENT ON COLUMN :"registry".tags.change_id IS 'ID of last change deployed before the tag was applied.'; +COMMENT ON COLUMN :"registry".tags.note IS 'Description of the tag.'; +COMMENT ON COLUMN :"registry".tags.committed_at IS 'Date the tag was applied to the database.'; +COMMENT ON COLUMN :"registry".tags.committer_name IS 'Name of the user who applied the tag.'; +COMMENT ON COLUMN :"registry".tags.committer_email IS 'Email address of the user who applied the tag.'; +COMMENT ON COLUMN :"registry".tags.planned_at IS 'Date the tag was added to the plan.'; +COMMENT ON COLUMN :"registry".tags.planner_name IS 'Name of the user who planed the tag.'; +COMMENT ON COLUMN :"registry".tags.planner_email IS 'Email address of the user who planned the tag.'; + +CREATE TABLE :"registry".dependencies ( + change_id TEXT NOT NULL REFERENCES :"registry".changes(change_id) ON UPDATE CASCADE ON DELETE CASCADE, + type TEXT NOT NULL, + dependency TEXT NOT NULL, + dependency_id TEXT NULL REFERENCES :"registry".changes(change_id) ON UPDATE CASCADE CONSTRAINT dependencies_check CHECK ( + (type = 'require' AND dependency_id IS NOT NULL) + OR (type = 'conflict' AND dependency_id IS NULL) + ), + PRIMARY KEY (change_id, dependency) +); + +COMMENT ON TABLE :"registry".dependencies IS 'Tracks the currently satisfied dependencies.'; +COMMENT ON COLUMN :"registry".dependencies.change_id IS 'ID of the depending change.'; +COMMENT ON COLUMN :"registry".dependencies.type IS 'Type of dependency.'; +COMMENT ON COLUMN :"registry".dependencies.dependency IS 'Dependency name.'; +COMMENT ON COLUMN :"registry".dependencies.dependency_id IS 'Change ID the dependency resolves to.'; + +CREATE TABLE :"registry".events ( + event TEXT NOT NULL CONSTRAINT events_event_check CHECK ( + event IN ('deploy', 'revert', 'fail', 'merge') + ), + change_id TEXT NOT NULL, + change TEXT NOT NULL, + project TEXT NOT NULL REFERENCES :"registry".projects(project) ON UPDATE CASCADE, + note TEXT NOT NULL DEFAULT '', + requires TEXT[] NOT NULL DEFAULT '{}', + conflicts TEXT[] NOT NULL DEFAULT '{}', + tags TEXT[] NOT NULL DEFAULT '{}', + committed_at TIMESTAMPTZ NOT NULL DEFAULT clock_timestamp(), + committer_name TEXT NOT NULL, + committer_email TEXT NOT NULL, + planned_at TIMESTAMPTZ NOT NULL, + planner_name TEXT NOT NULL, + planner_email TEXT NOT NULL, + PRIMARY KEY (change_id, committed_at) +); + +COMMENT ON TABLE :"registry".events IS 'Contains full history of all deployment events.'; +COMMENT ON COLUMN :"registry".events.event IS 'Type of event.'; +COMMENT ON COLUMN :"registry".events.change_id IS 'Change ID.'; +COMMENT ON COLUMN :"registry".events.change IS 'Change name.'; +COMMENT ON COLUMN :"registry".events.project IS 'Name of the Sqitch project to which the change belongs.'; +COMMENT ON COLUMN :"registry".events.note IS 'Description of the change.'; +COMMENT ON COLUMN :"registry".events.requires IS 'Array of the names of required changes.'; +COMMENT ON COLUMN :"registry".events.conflicts IS 'Array of the names of conflicting changes.'; +COMMENT ON COLUMN :"registry".events.tags IS 'Tags associated with the change.'; +COMMENT ON COLUMN :"registry".events.committed_at IS 'Date the event was committed.'; +COMMENT ON COLUMN :"registry".events.committer_name IS 'Name of the user who committed the event.'; +COMMENT ON COLUMN :"registry".events.committer_email IS 'Email address of the user who committed the event.'; +COMMENT ON COLUMN :"registry".events.planned_at IS 'Date the event was added to the plan.'; +COMMENT ON COLUMN :"registry".events.planner_name IS 'Name of the user who planed the change.'; +COMMENT ON COLUMN :"registry".events.planner_email IS 'Email address of the user who plan planned the change.'; diff --git a/lib/App/Sqitch/Engine/exasol.pm b/lib/App/Sqitch/Engine/exasol.pm index 6318390f9..e839fbb9e 100644 --- a/lib/App/Sqitch/Engine/exasol.pm +++ b/lib/App/Sqitch/Engine/exasol.pm @@ -181,7 +181,7 @@ sub _ts2char_format { sub _ts_default { 'current_timestamp' } sub _listagg_format { - return q{GROUP_CONCAT(%s SEPARATOR ' ')}; + return q{GROUP_CONCAT(%1$s ORDER BY %1$s SEPARATOR ' ')}; } sub _regex_op { 'REGEXP_LIKE' } @@ -568,7 +568,7 @@ David E. Wheeler =head1 License -Copyright (c) 2012-2021 iovation Inc., David E. Wheeler +Copyright (c) 2012-2022 iovation Inc., David E. Wheeler Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/lib/App/Sqitch/Engine/firebird.pm b/lib/App/Sqitch/Engine/firebird.pm index 3cd5e8007..95274892a 100644 --- a/lib/App/Sqitch/Engine/firebird.pm +++ b/lib/App/Sqitch/Engine/firebird.pm @@ -66,6 +66,7 @@ has dbh => ( is => 'rw', isa => DBH, lazy => 1, + clearer => '_clear_dbh', default => sub { my $self = shift; my $uri = $self->registry_uri; @@ -288,7 +289,7 @@ sub _no_table_error { } sub _no_column_error { - return $DBI::errstr && $DBI::errstr =~ /^-Column unknown|/m; + return $DBI::errstr && $DBI::errstr =~ /^-Column unknown/m; } sub _regex_op { 'SIMILAR TO' } # NOT good match for @@ -343,6 +344,13 @@ sub run_upgrade { my @cmd = $self->isql; $cmd[-1] = $self->connection_string($uri); my $sqitch = $self->sqitch; + unless ($uri->host) { + # Only one connection allowed when using an embedded database (Engine 12 + # provider). So disconnect so that the upgrade can connect and succeed, + # and clear the disconnected handle so that the next call to ->dbh will + # reconnect. + $self->dbh->disconnect; $self->_clear_dbh; + } $sqitch->run( @cmd, '-input' => $sqitch->quote_shell($file) ); } @@ -890,7 +898,8 @@ sub default_client { my $loops = 0; for my $dir (File::Spec->path) { my $path = file $dir, $try; - $path = Win32::GetShortPathName($path) if App::Sqitch::ISWIN; + # GetShortPathName returns undef for nonexistent files. + $path = Win32::GetShortPathName($path) // next if App::Sqitch::ISWIN; if (-f $path && -x $path) { if (try { App::Sqitch->probe($path, @opts) =~ /Firebird/ } ) { # Restore STDERR and return. @@ -973,7 +982,7 @@ David E. Wheeler =head1 License -Copyright (c) 2012-2021 iovation Inc., David E. Wheeler +Copyright (c) 2012-2022 iovation Inc., David E. Wheeler Copyright (c) 2013 ศ˜tefan Suciu diff --git a/lib/App/Sqitch/Engine/mysql.pm b/lib/App/Sqitch/Engine/mysql.pm index 64f0a519a..551ed873b 100644 --- a/lib/App/Sqitch/Engine/mysql.pm +++ b/lib/App/Sqitch/Engine/mysql.pm @@ -121,7 +121,7 @@ has dbh => ( # Make sure we support this version. my ($dbms, $vnum, $vstr) = $dbh->{mysql_serverinfo} =~ /mariadb/i ? ('MariaDB', 50300, '5.3') - : ('MySQL', 50000, '5.0.0'); + : ('MySQL', 50100, '5.1.0'); hurl mysql => __x( 'Sqitch requires {rdbms} {want_version} or higher; this is {have_version}', rdbms => $dbms, @@ -223,8 +223,9 @@ has _fractional_seconds => ( lazy => 1, default => sub { my $dbh = shift->dbh; - return $dbh->{mysql_serverinfo} !~ /mariadb/i - && $dbh->{mysql_serverversion} >= 50604; + return $dbh->{mysql_serverinfo} =~ /mariadb/i + ? $dbh->{mysql_serverversion} >= 50305 + : $dbh->{mysql_serverversion} >= 50604; }, ); @@ -250,23 +251,29 @@ sub _quote_idents { sub _version_query { 'SELECT CAST(ROUND(MAX(version), 1) AS CHAR) FROM releases' } -sub initialized { - my $self = shift; +has initialized => ( + is => 'ro', + isa => Bool, + lazy => 1, + writer => '_set_initialized', + default => sub { + my $self = shift; - # Try to connect. - my $dbh = try { $self->dbh } catch { - # MySQL error code 1049 (ER_BAD_DB_ERROR): Unknown database '%-.192s' - return if $DBI::err && $DBI::err == 1049; - die $_; - } or return 0; - - return $dbh->selectcol_arrayref(q{ - SELECT COUNT(*) - FROM information_schema.tables - WHERE table_schema = ? - AND table_name = ? - }, undef, $self->registry, 'changes')->[0]; -} + # Try to connect. + my $dbh = try { $self->dbh } catch { + # MySQL error code 1049 (ER_BAD_DB_ERROR): Unknown database '%-.192s' + return if $DBI::err && $DBI::err == 1049; + die $_; + } or return 0; + + return $dbh->selectcol_arrayref(q{ + SELECT COUNT(*) + FROM information_schema.tables + WHERE table_schema = ? + AND table_name = ? + }, undef, $self->registry, 'changes')->[0]; + } +); sub initialize { my $self = shift; @@ -286,6 +293,7 @@ sub initialize { # Deploy the registry to the Sqitch database. $self->run_upgrade( file(__FILE__)->dir->file('mysql.sql') ); + $self->_set_initialized(1); $self->_register_release; } @@ -306,7 +314,10 @@ sub begin_work { # Override to try to acquire a lock on the string "sqitch working" without # waiting. sub try_lock { - shift->dbh->selectcol_arrayref( + my $self = shift; + # Can't create a lock in the registry if it doesn't exist. + $self->initialize unless $self->initialized; + $self->dbh->selectcol_arrayref( q{SELECT get_lock('sqitch working', 0)} )->[0] } @@ -348,7 +359,7 @@ sub _regex_op { 'REGEXP' } sub _limit_default { '18446744073709551615' } sub _listagg_format { - return q{GROUP_CONCAT(%s SEPARATOR ' ')}; + return q{GROUP_CONCAT(%1$s ORDER BY %1$s SEPARATOR ' ')}; } sub _prepare_to_log { @@ -431,7 +442,6 @@ sub run_verify { sub run_upgrade { my ($self, $file) = @_; - my $dbh = $self->dbh; my @cmd = $self->mysql; $cmd[1 + firstidx { $_ eq '--database' } @cmd ] = $self->registry; return $self->sqitch->run( @cmd, $self->_source($file) ) @@ -441,7 +451,8 @@ sub run_upgrade { (my $sql = scalar $file->slurp) =~ s{DATETIME\(\d+\)}{DATETIME}g; # Strip out 5.5 stuff on earlier versions. - $sql =~ s/-- ## BEGIN 5[.]5.+?-- ## END 5[.]5//ms if $dbh->{mysql_serverversion} < 50500; + $sql =~ s/-- ## BEGIN 5[.]5.+?-- ## END 5[.]5//ms + if $self->dbh->{mysql_serverversion} < 50500; # Write out a temp file and execute it. require File::Temp; @@ -547,7 +558,7 @@ David E. Wheeler =head1 License -Copyright (c) 2012-2021 iovation Inc., David E. Wheeler +Copyright (c) 2012-2022 iovation Inc., David E. Wheeler Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/lib/App/Sqitch/Engine/oracle.pm b/lib/App/Sqitch/Engine/oracle.pm index f3bcdc7a4..32d70f57c 100644 --- a/lib/App/Sqitch/Engine/oracle.pm +++ b/lib/App/Sqitch/Engine/oracle.pm @@ -423,19 +423,22 @@ sub is_deployed_tag { sub are_deployed_changes { my $self = shift; + @{ $self->dbh->selectcol_arrayref( + 'SELECT change_id FROM changes WHERE ' . _change_id_in(scalar @_), + undef, + map { $_->id } @_, + ) }; +} + +sub _change_id_in { + my $i = shift; my @qs; - my $i = @_; while ($i > 250) { push @qs => 'change_id IN (' . join(', ' => ('?') x 250) . ')'; $i -= 250; } push @qs => 'change_id IN (' . join(', ' => ('?') x $i) . ')' if $i > 0; - my $expr = join ' OR ', @qs; - @{ $self->dbh->selectcol_arrayref( - "SELECT change_id FROM changes WHERE $expr", - undef, - map { $_->id } @_, - ) }; + return join ' OR ', @qs; } sub _registry_variable { @@ -809,7 +812,7 @@ David E. Wheeler =head1 License -Copyright (c) 2012-2021 iovation Inc., David E. Wheeler +Copyright (c) 2012-2022 iovation Inc., David E. Wheeler Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/lib/App/Sqitch/Engine/pg.pm b/lib/App/Sqitch/Engine/pg.pm index 2136eae76..a4aa7bef7 100644 --- a/lib/App/Sqitch/Engine/pg.pm +++ b/lib/App/Sqitch/Engine/pg.pm @@ -11,6 +11,7 @@ use Locale::TextDomain qw(App-Sqitch); use App::Sqitch::Plan::Change; use List::Util qw(first); use App::Sqitch::Types qw(DBH ArrayRef); +use Type::Utils qw(enum); use namespace::autoclean; extends 'App::Sqitch::Engine'; @@ -60,7 +61,7 @@ has _psql => ( [ user => $self->username ], [ dbname => $uri->dbname ], [ host => $uri->host ], - [ port => $uri->_port ], + [ port => $uri->port ], map { [ $_ => $query_params{$_} ] } sort keys %query_params, ) { @@ -101,6 +102,13 @@ sub name { 'PostgreSQL' } sub driver { 'DBD::Pg 2.0' } sub default_client { 'psql' } +has _provider => ( + is => 'rw', + isa => enum([qw( postgres yugabyte )]), + default => 'postgres', + lazy => 1, +); + has dbh => ( is => 'rw', isa => DBH, @@ -135,6 +143,12 @@ has dbh => ( # https://www.nntp.perl.org/group/perl.dbi.dev/2013/11/msg7622.html $dbh->set_err(undef, undef) if $dbh->err; }; + # Determine the provider. Yugabyte says this is the right way to do it. + # https://yugabyte-db.slack.com/archives/CG0KQF0GG/p1653762283847589 + my $v = $dbh->selectcol_arrayref( + q{SELECT split_part(version(), ' ', 2)} + )->[0] // ''; + $self->_provider('yugabyte') if $v =~ /-YB-/; return; }, }, @@ -166,7 +180,16 @@ sub _ts_default { 'clock_timestamp()' } sub _char2ts { $_[1]->as_string(format => 'iso') } sub _listagg_format { - q{ARRAY(SELECT * FROM UNNEST( array_agg(%s) ) a WHERE a IS NOT NULL)} + my $dbh = shift->dbh; + # Since 9.3, we can use array_remove(). + return q{array_remove(array_agg(%1$s ORDER BY %1$s), NULL)} + if $dbh->{pg_server_version} >= 90300; + + # Since 8.4 we can use ORDER BY. + return q{ARRAY(SELECT * FROM UNNEST( array_agg(%1$s ORDER BY %1$s) ) a WHERE a IS NOT NULL)} + if $dbh->{pg_server_version} >= 80400; + + return q{ARRAY(SELECT * FROM UNNEST( array_agg(%s) ) a WHERE a IS NOT NULL)}; } sub _regex_op { '~' } @@ -189,7 +212,7 @@ sub initialize { 'Sqitch schema "{schema}" already exists', schema => $self->registry ) if $self->initialized; - $self->_run_registry_file( file(__FILE__)->dir->file('pg.sql') ); + $self->_run_registry_file( file(__FILE__)->dir->file($self->key . '.sql') ); $self->_register_release; } @@ -259,7 +282,10 @@ sub begin_work { # Start transaction and lock changes to allow only one change at a time. $dbh->begin_work; - $dbh->do('LOCK TABLE changes IN EXCLUSIVE MODE'); + $dbh->do('LOCK TABLE changes IN EXCLUSIVE MODE') + if $self->_provider eq 'postgres'; + # Yugabyte does not yet support EXCLUSIVE MODE. + # https://docs.yugabyte.com/preview/api/ysql/the-sql-language/statements/txn_lock/#lockmode-1 return $self; } @@ -274,6 +300,13 @@ sub try_lock { # until timeout. sub wait_lock { my $self = shift; + + # Yugabyte does not support advisory locks. + # https://github.com/yugabyte/yugabyte-db/issues/3642 + # Use pessimistic locking when it becomes available. + # https://github.com/yugabyte/yugabyte-db/issues/5680 + return 1 if $self->_provider ne 'postgres'; + # Asynchronously request a lock with an indefinite wait. my $dbh = $self->dbh; $dbh->do( @@ -339,7 +372,7 @@ sub log_new_tags { , planner_email ) SELECT tid, tg, proj, chid, n, name, email, at, pname, pemail FROM ( VALUES - } . join( ",\n ", ( q{(?, ?, ?, ?, ?, ?, ?, ?::timestamptz, ?, ?)} ) x @tags ) + } . join( ",\n ", ( q{(?::text, ?::text, ?::text, ?::text, ?::text, ?::text, ?::text, ?::timestamptz, ?::text, ?::text)} ) x @tags ) . q{ ) i(tid, tg, proj, chid, n, name, email, at, pname, pemail) LEFT JOIN tags ON i.tid = tags.tag_id @@ -408,6 +441,13 @@ sub _dt($) { sub _no_table_error { return 0 unless $DBI::state && $DBI::state eq '42P01'; # undefined_table my $dbh = shift->dbh; + return 1 unless $dbh->{pg_server_version} >= 90000; + + # Try to avoid confusion for people monitoring the Postgres error log by + # sending a warning to the log immediately after the missing relation error + # to tell log watchers that Sqitch is aware of the issue and will next + # initialize the database. Hopefully this will reduce confusion and + # unnecessary time trouble shooting an error that Sqitch handles. my @msg = map { $dbh->quote($_) } ( __ 'Sqitch registry not initialized', __ 'Because the "changes" table does not exist, Sqitch will now initialize the database to create its registry tables.', @@ -417,7 +457,7 @@ sub _no_table_error { SET LOCAL client_min_messages = 'ERROR'; RAISE WARNING USING ERRCODE = 'undefined_table', MESSAGE = %s, DETAIL = %s; END; - $$}, @msg) if $dbh->{pg_server_version} >= 90000; + $$}, @msg); return 1; } @@ -478,7 +518,7 @@ App::Sqitch::Engine::pg - Sqitch PostgreSQL Engine =head1 Description App::Sqitch::Engine::pg provides the PostgreSQL storage engine for Sqitch. It -supports PostgreSQL 8.4.0 and higher as well as Postgres-XC 1.2 and higher. +supports PostgreSQL 8.4.0 and higher, Postgres-XC 1.2 and higher, and YugabyteDB. =head1 Interface @@ -508,7 +548,7 @@ David E. Wheeler =head1 License -Copyright (c) 2012-2021 iovation Inc., David E. Wheeler +Copyright (c) 2012-2022 iovation Inc., David E. Wheeler Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/lib/App/Sqitch/Engine/snowflake.pm b/lib/App/Sqitch/Engine/snowflake.pm index 773996179..94884136f 100644 --- a/lib/App/Sqitch/Engine/snowflake.pm +++ b/lib/App/Sqitch/Engine/snowflake.pm @@ -277,7 +277,7 @@ sub _verbose_opts { # * Scalar variables like the array constructor can't be used in WHERE clauses # https://support.snowflake.net/s/case/5000Z000010wX7yQAE/ sub _listagg_format { - return q{listagg(%s, ' ')}; + return q{listagg(%1$s, ' ') WITHIN GROUP (ORDER BY %1$s)}; } sub _ts_default { 'current_timestamp' } @@ -708,7 +708,7 @@ David E. Wheeler =head1 License -Copyright (c) 2012-2021 iovation Inc., David E. Wheeler +Copyright (c) 2012-2022 iovation Inc., David E. Wheeler Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/lib/App/Sqitch/Engine/sqlite.pm b/lib/App/Sqitch/Engine/sqlite.pm index 6513453c8..b5526b63f 100644 --- a/lib/App/Sqitch/Engine/sqlite.pm +++ b/lib/App/Sqitch/Engine/sqlite.pm @@ -172,7 +172,7 @@ sub _no_table_error { } sub _no_column_error { - return try { $_->message =~ /^\Qno such column:/ }; + return $DBI::errstr && $DBI::errstr =~ /^\Qno such column:/; } sub _regex_op { 'REGEXP' } @@ -188,6 +188,8 @@ sub _ts2char_format { } sub _listagg_format { + # The order of the concatenated elements is arbitrary. + # https://www.sqlite.org/lang_aggfunc.html return q{group_concat(%s, ' ')}; } @@ -282,7 +284,7 @@ David E. Wheeler =head1 License -Copyright (c) 2012-2021 iovation Inc., David E. Wheeler +Copyright (c) 2012-2022 iovation Inc., David E. Wheeler Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/lib/App/Sqitch/Engine/vertica.pm b/lib/App/Sqitch/Engine/vertica.pm index d4c5b8d5a..6fb45888f 100644 --- a/lib/App/Sqitch/Engine/vertica.pm +++ b/lib/App/Sqitch/Engine/vertica.pm @@ -259,7 +259,6 @@ sub _select_state { sub current_state { my ( $self, $project ) = @_; - my $dbh = $self->dbh; my $state = try { $self->_select_state($project, 1) } catch { @@ -268,7 +267,7 @@ sub current_state { die $_; } or return undef; - $state->{tags} = $dbh->selectcol_arrayref( + $state->{tags} = $self->dbh->selectcol_arrayref( 'SELECT tag FROM tags WHERE change_id = ? ORDER BY committed_at', undef, $state->{change_id} ); @@ -562,7 +561,7 @@ David E. Wheeler =head1 License -Copyright (c) 2012-2021 iovation Inc., David E. Wheeler +Copyright (c) 2012-2022 iovation Inc., David E. Wheeler Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/lib/App/Sqitch/ItemFormatter.pm b/lib/App/Sqitch/ItemFormatter.pm index 771ff37fa..5a9c8064b 100644 --- a/lib/App/Sqitch/ItemFormatter.pm +++ b/lib/App/Sqitch/ItemFormatter.pm @@ -584,7 +584,7 @@ David E. Wheeler =head1 License -Copyright (c) 2012-2021 iovation Inc., David E. Wheeler +Copyright (c) 2012-2022 iovation Inc., David E. Wheeler Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/lib/App/Sqitch/Plan.pm b/lib/App/Sqitch/Plan.pm index 89ffc9bec..2030016a7 100644 --- a/lib/App/Sqitch/Plan.pm +++ b/lib/App/Sqitch/Plan.pm @@ -1597,7 +1597,7 @@ David E. Wheeler =head1 License -Copyright (c) 2012-2021 iovation Inc., David E. Wheeler +Copyright (c) 2012-2022 iovation Inc., David E. Wheeler Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/lib/App/Sqitch/Plan/Blank.pm b/lib/App/Sqitch/Plan/Blank.pm index 081c21dc1..16931e2c0 100644 --- a/lib/App/Sqitch/Plan/Blank.pm +++ b/lib/App/Sqitch/Plan/Blank.pm @@ -39,7 +39,7 @@ David E. Wheeler =head1 License -Copyright (c) 2012-2021 iovation Inc., David E. Wheeler +Copyright (c) 2012-2022 iovation Inc., David E. Wheeler Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/lib/App/Sqitch/Plan/Change.pm b/lib/App/Sqitch/Plan/Change.pm index a93bfaf9f..d0538f4e9 100644 --- a/lib/App/Sqitch/Plan/Change.pm +++ b/lib/App/Sqitch/Plan/Change.pm @@ -647,7 +647,7 @@ David E. Wheeler =head1 License -Copyright (c) 2012-2021 iovation Inc., David E. Wheeler +Copyright (c) 2012-2022 iovation Inc., David E. Wheeler Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/lib/App/Sqitch/Plan/ChangeList.pm b/lib/App/Sqitch/Plan/ChangeList.pm index 9e03984e3..7f674b4d5 100644 --- a/lib/App/Sqitch/Plan/ChangeList.pm +++ b/lib/App/Sqitch/Plan/ChangeList.pm @@ -410,7 +410,7 @@ David E. Wheeler =head1 License -Copyright (c) 2012-2021 iovation Inc., David E. Wheeler +Copyright (c) 2012-2022 iovation Inc., David E. Wheeler Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/lib/App/Sqitch/Plan/Depend.pm b/lib/App/Sqitch/Plan/Depend.pm index 3b69e0a5b..336e68644 100644 --- a/lib/App/Sqitch/Plan/Depend.pm +++ b/lib/App/Sqitch/Plan/Depend.pm @@ -366,7 +366,7 @@ David E. Wheeler =head1 License -Copyright (c) 2012-2021 iovation Inc., David E. Wheeler +Copyright (c) 2012-2022 iovation Inc., David E. Wheeler Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/lib/App/Sqitch/Plan/Line.pm b/lib/App/Sqitch/Plan/Line.pm index da2d0ace7..b2b9409ad 100644 --- a/lib/App/Sqitch/Plan/Line.pm +++ b/lib/App/Sqitch/Plan/Line.pm @@ -347,7 +347,7 @@ David E. Wheeler =head1 License -Copyright (c) 2012-2021 iovation Inc., David E. Wheeler +Copyright (c) 2012-2022 iovation Inc., David E. Wheeler Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/lib/App/Sqitch/Plan/LineList.pm b/lib/App/Sqitch/Plan/LineList.pm index bbd793c0e..9f22a3a41 100644 --- a/lib/App/Sqitch/Plan/LineList.pm +++ b/lib/App/Sqitch/Plan/LineList.pm @@ -110,7 +110,7 @@ David E. Wheeler =head1 License -Copyright (c) 2012-2021 iovation Inc., David E. Wheeler +Copyright (c) 2012-2022 iovation Inc., David E. Wheeler Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/lib/App/Sqitch/Plan/Pragma.pm b/lib/App/Sqitch/Plan/Pragma.pm index 6ac3af22a..55c0bb70e 100644 --- a/lib/App/Sqitch/Plan/Pragma.pm +++ b/lib/App/Sqitch/Plan/Pragma.pm @@ -102,7 +102,7 @@ David E. Wheeler =head1 License -Copyright (c) 2012-2021 iovation Inc., David E. Wheeler +Copyright (c) 2012-2022 iovation Inc., David E. Wheeler Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/lib/App/Sqitch/Plan/Tag.pm b/lib/App/Sqitch/Plan/Tag.pm index 8e6133312..5145f7e92 100644 --- a/lib/App/Sqitch/Plan/Tag.pm +++ b/lib/App/Sqitch/Plan/Tag.pm @@ -158,7 +158,7 @@ David E. Wheeler =head1 License -Copyright (c) 2012-2021 iovation Inc., David E. Wheeler +Copyright (c) 2012-2022 iovation Inc., David E. Wheeler Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/lib/App/Sqitch/Role/ConnectingCommand.pm b/lib/App/Sqitch/Role/ConnectingCommand.pm index 32146392d..447735b59 100644 --- a/lib/App/Sqitch/Role/ConnectingCommand.pm +++ b/lib/App/Sqitch/Role/ConnectingCommand.pm @@ -120,7 +120,7 @@ David E. Wheeler =head1 License -Copyright (c) 2012-2021 iovation Inc., David E. Wheeler +Copyright (c) 2012-2022 iovation Inc., David E. Wheeler Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/lib/App/Sqitch/Role/ContextCommand.pm b/lib/App/Sqitch/Role/ContextCommand.pm index ae285efe6..4d8d899dd 100644 --- a/lib/App/Sqitch/Role/ContextCommand.pm +++ b/lib/App/Sqitch/Role/ContextCommand.pm @@ -122,7 +122,7 @@ David E. Wheeler =head1 License -Copyright (c) 2012-2021 iovation Inc., David E. Wheeler +Copyright (c) 2012-2022 iovation Inc., David E. Wheeler Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/lib/App/Sqitch/Role/DBIEngine.pm b/lib/App/Sqitch/Role/DBIEngine.pm index 2196104d9..f63b7ca6d 100644 --- a/lib/App/Sqitch/Role/DBIEngine.pm +++ b/lib/App/Sqitch/Role/DBIEngine.pm @@ -1113,7 +1113,7 @@ David E. Wheeler =head1 License -Copyright (c) 2012-2021 iovation Inc., David E. Wheeler +Copyright (c) 2012-2022 iovation Inc., David E. Wheeler Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/lib/App/Sqitch/Role/RevertDeployCommand.pm b/lib/App/Sqitch/Role/RevertDeployCommand.pm index 56a2a621c..2ae01e604 100644 --- a/lib/App/Sqitch/Role/RevertDeployCommand.pm +++ b/lib/App/Sqitch/Role/RevertDeployCommand.pm @@ -263,7 +263,7 @@ David E. Wheeler =head1 License -Copyright (c) 2012-2021 iovation Inc., David E. Wheeler +Copyright (c) 2012-2022 iovation Inc., David E. Wheeler Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/lib/App/Sqitch/Role/TargetConfigCommand.pm b/lib/App/Sqitch/Role/TargetConfigCommand.pm index e801a5c62..a48684a0e 100644 --- a/lib/App/Sqitch/Role/TargetConfigCommand.pm +++ b/lib/App/Sqitch/Role/TargetConfigCommand.pm @@ -530,7 +530,7 @@ David E. Wheeler =head1 License -Copyright (c) 2012-2021 iovation Inc., David E. Wheeler +Copyright (c) 2012-2022 iovation Inc., David E. Wheeler Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/lib/App/Sqitch/Target.pm b/lib/App/Sqitch/Target.pm index 17ce2d5e7..a8e50654a 100644 --- a/lib/App/Sqitch/Target.pm +++ b/lib/App/Sqitch/Target.pm @@ -842,7 +842,7 @@ David E. Wheeler =head1 License -Copyright (c) 2012-2021 iovation Inc., David E. Wheeler +Copyright (c) 2012-2022 iovation Inc., David E. Wheeler Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/lib/App/Sqitch/Types.pm b/lib/App/Sqitch/Types.pm index 3ab629ad0..6a3df691f 100644 --- a/lib/App/Sqitch/Types.pm +++ b/lib/App/Sqitch/Types.pm @@ -168,7 +168,7 @@ David E. Wheeler =head1 License -Copyright (c) 2012-2021 iovation Inc., David E. Wheeler +Copyright (c) 2012-2022 iovation Inc., David E. Wheeler Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/lib/App/Sqitch/X.pm b/lib/App/Sqitch/X.pm index 3f1a98544..6d4120996 100644 --- a/lib/App/Sqitch/X.pm +++ b/lib/App/Sqitch/X.pm @@ -175,7 +175,7 @@ David E. Wheeler =head1 License -Copyright (c) 2012-2021 iovation Inc., David E. Wheeler +Copyright (c) 2012-2022 iovation Inc., David E. Wheeler Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/lib/sqitch-authentication.pod b/lib/sqitch-authentication.pod index 4f1f5646a..028322335 100644 --- a/lib/sqitch-authentication.pod +++ b/lib/sqitch-authentication.pod @@ -33,11 +33,10 @@ follows: =over -=item PostgreSQL - -The Postgres engine uses the C environment variable, if set. -Otherwise, it uses the system username. +=item PostgreSQL, YugabyteDB, CockroachDB +The Postgres, Yugabyte, and Cockroach engines use the C environment +variable, if set. Otherwise, it uses the system username. =item MySQL @@ -199,9 +198,9 @@ the feature. Here's how the database engines supported by Sqitch shake out: =over -=item PostgreSQL +=item PostgreSQL, YugabyteDB, CockroachDB -PostgreSQL will use a +PostgreSQL, YugabyteDB, and CockroachDB will use a L file|https://www.postgresql.org/docs/current/static/libpq-pgpass.html> in the user's home directory to or referenced by the C<$PGPASSFILE> environment variable. This file must limit access only to the current user (C<0600>) and @@ -331,7 +330,7 @@ you may wish to use instead. However, their behaviors may not be consistent: =over -=item PostgreSQL +=item PostgreSQL, YugabyteDB, CockroachDB C<$PGPASSWORD> diff --git a/lib/sqitch-bundle.pod b/lib/sqitch-bundle.pod index 633e71289..67362dd47 100644 --- a/lib/sqitch-bundle.pod +++ b/lib/sqitch-bundle.pod @@ -4,7 +4,7 @@ sqitch-bundle - Bundle a Sqitch project for distribution =head1 Synopsis - sqitch bundle [options + sqitch bundle [options] sqitch bundle --dest-dir widgets-1.0.0 sqitch bundle --all sqitch bundle pg mysql diff --git a/lib/sqitch-config.pod b/lib/sqitch-config.pod index f15ae95ef..320f9fc08 100644 --- a/lib/sqitch-config.pod +++ b/lib/sqitch-config.pod @@ -437,7 +437,7 @@ The database engine to use. Supported engines include: =over -=item * C - L and L +=item * C - L, L, and L =item * C - L @@ -453,6 +453,8 @@ The database engine to use. Supported engines include: =item * C - L +=item * C - L + =back =item C diff --git a/lib/sqitch-engine-usage.pod b/lib/sqitch-engine-usage.pod index 78b7af119..a09a3aebd 100644 --- a/lib/sqitch-engine-usage.pod +++ b/lib/sqitch-engine-usage.pod @@ -10,7 +10,6 @@ sqitch-engine-usage - Sqitch engine usage statement sqitch engine alter [engine-options] sqitch engine remove sqitch engine show - sqitch engine update-config =head1 Options diff --git a/lib/sqitch-engine.pod b/lib/sqitch-engine.pod index acda9e14d..1d6ddc1d6 100644 --- a/lib/sqitch-engine.pod +++ b/lib/sqitch-engine.pod @@ -10,7 +10,6 @@ sqitch-engine - Manage database engine configuration sqitch engine alter [engine-options] sqitch engine remove sqitch engine show - sqitch engine update-config =head1 Description @@ -252,11 +251,6 @@ Gives some information about the engine C<< >>, including the associated properties. Specify multiple engine names to see information for each. -=head2 C - -Update the configuration from a configuration file that predates the addition -of the C command to Sqitch. - =head1 Configuration Variables The engines are stored in the configuration file, but the command itself diff --git a/lib/sqitch-environment.pod b/lib/sqitch-environment.pod index 2cad235f7..008c95ddd 100644 --- a/lib/sqitch-environment.pod +++ b/lib/sqitch-environment.pod @@ -110,7 +110,7 @@ support environment variables of their own. These are not comprehensive for all variables supported by a database engine, but document those supported by Sqitch's implementation for each engine. -=head3 PostgreSQL +=head3 PostgreSQL, YugabyteDB, CockroachDB All the usual L @@ -249,12 +249,12 @@ C<$SQITCH_PASSWORD> and the target URI password. =item C -The PostgreSQL server host to connect to. Superseded by the target URI host +The Vertica server host to connect to. Superseded by the target URI host name. =item C -The PostgreSQL server port to connect to. Superseded by the target URI port. +The Vertica server port to connect to. Superseded by the target URI port. =item C diff --git a/lib/sqitch-init.pod b/lib/sqitch-init.pod index 18795a5af..68fce7500 100644 --- a/lib/sqitch-init.pod +++ b/lib/sqitch-init.pod @@ -37,7 +37,7 @@ include: =over -=item * C - L and L +=item * C - C - L, L, and L =item * C - L @@ -53,6 +53,8 @@ include: =item * C - L +=item * C - L + =back =item C<--top-dir> diff --git a/lib/sqitch.pod b/lib/sqitch.pod index c13ab93ac..9ec4d4d9c 100644 --- a/lib/sqitch.pod +++ b/lib/sqitch.pod @@ -10,9 +10,37 @@ sqitch - Sensible database change management =head1 Description -Sqitch is a database change management application. What makes it different -from your typical L-L -approaches? A few things: +Sqitch is a database change management application. It currently supports: + +=over + +=item * L 8.4+ + +=item * L 2.6+ + +=item * L 21+ + +=item * L 3.7.11+ + +=item * L 5.1+ + +=item * L 10.0+ + +=item * L 10g+, + +=item * L 2.0+ + +=item * L 6.0+ + +=item * L 6.0+ + +=item * L + +=back + +What makes it different from your typical +L-L approaches? A few +things: =over @@ -97,7 +125,7 @@ of a database with Sqitch. =over -=item * L +=item * L =item * L @@ -467,7 +495,7 @@ David E. Wheeler =head1 License -Copyright (c) 2012-2021 iovation Inc., David E. Wheeler +Copyright (c) 2012-2022 iovation Inc., David E. Wheeler Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/lib/sqitchtutorial-exasol.pod b/lib/sqitchtutorial-exasol.pod index c1d10ec91..ee365975c 100644 --- a/lib/sqitchtutorial-exasol.pod +++ b/lib/sqitchtutorial-exasol.pod @@ -1391,7 +1391,7 @@ David E. Wheeler =head1 License -Copyright (c) 2012-2021 iovation Inc., David E. Wheeler +Copyright (c) 2012-2022 iovation Inc., David E. Wheeler Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/lib/sqitchtutorial-firebird.pod b/lib/sqitchtutorial-firebird.pod index 32d176de9..8f5859e65 100644 --- a/lib/sqitchtutorial-firebird.pod +++ b/lib/sqitchtutorial-firebird.pod @@ -1243,7 +1243,7 @@ is planned to make managing idempotent reworkings even easier. Stay tuned. =head1 License -Copyright (c) 2012-2021 iovation Inc., David E. Wheeler +Copyright (c) 2012-2022 iovation Inc., David E. Wheeler Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/lib/sqitchtutorial-mysql.pod b/lib/sqitchtutorial-mysql.pod index 55d25e787..d2287c95b 100644 --- a/lib/sqitchtutorial-mysql.pod +++ b/lib/sqitchtutorial-mysql.pod @@ -1703,7 +1703,7 @@ David E. Wheeler =head1 License -Copyright (c) 2012-2021 iovation Inc., David E. Wheeler +Copyright (c) 2012-2022 iovation Inc., David E. Wheeler Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/lib/sqitchtutorial-oracle.pod b/lib/sqitchtutorial-oracle.pod index bf67e93a4..386f856af 100644 --- a/lib/sqitchtutorial-oracle.pod +++ b/lib/sqitchtutorial-oracle.pod @@ -1963,7 +1963,7 @@ David E. Wheeler =head1 License -Copyright (c) 2012-2021 iovation Inc., David E. Wheeler +Copyright (c) 2012-2022 iovation Inc., David E. Wheeler Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/lib/sqitchtutorial-snowflake.pod b/lib/sqitchtutorial-snowflake.pod index 38b9d5130..a28c1f61d 100644 --- a/lib/sqitchtutorial-snowflake.pod +++ b/lib/sqitchtutorial-snowflake.pod @@ -1399,7 +1399,7 @@ David E. Wheeler =head1 License -Copyright (c) 2012-2021 iovation Inc., David E. Wheeler +Copyright (c) 2012-2022 iovation Inc., David E. Wheeler Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/lib/sqitchtutorial-sqlite.pod b/lib/sqitchtutorial-sqlite.pod index 25e730979..7c878470d 100644 --- a/lib/sqitchtutorial-sqlite.pod +++ b/lib/sqitchtutorial-sqlite.pod @@ -210,7 +210,7 @@ sqitch database. In this case, we should end up with two databases: The Sqitch registry database. -=item * F +=item * F The database Sqitch manages. @@ -1220,7 +1220,7 @@ David E. Wheeler =head1 License -Copyright (c) 2012-2021 iovation Inc., David E. Wheeler +Copyright (c) 2012-2022 iovation Inc., David E. Wheeler Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/lib/sqitchtutorial-vertica.pod b/lib/sqitchtutorial-vertica.pod index 8eceebec8..a0aca99cd 100644 --- a/lib/sqitchtutorial-vertica.pod +++ b/lib/sqitchtutorial-vertica.pod @@ -1370,7 +1370,7 @@ David E. Wheeler =head1 License -Copyright (c) 2012-2021 iovation Inc., David E. Wheeler +Copyright (c) 2012-2022 iovation Inc., David E. Wheeler Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/lib/sqitchtutorial.pod b/lib/sqitchtutorial.pod index bd3713a1c..c1fc236a2 100644 --- a/lib/sqitchtutorial.pod +++ b/lib/sqitchtutorial.pod @@ -17,7 +17,9 @@ changes remain in sync and in the proper order. We'll start by creating a new project from scratch, a fictional antisocial networking site called Flipr. All examples use L as the VCS and L as the storage engine, -but for the most part you can substitute other VCSes and database engines in +though L and +L should work just as well. +For the most part you can substitute other VCSes and database engines in the examples as appropriate. If you'd like to manage an SQLite database, see L. @@ -79,7 +81,11 @@ Good, it picked up on the fact that we're creating changes for the PostgreSQL engine, thanks to the C<--engine pg> option, and saved it to the file. Furthermore, it wrote a commented-out C<[engine "pg"]> section with all the available PostgreSQL engine-specific settings commented out and ready to be -edited as appropriate. +edited as appropriate. This configuration will also fork for +L databases, too. For +L, use +C<--engine cockroach>, instead, and replace any instance of C with +C in the rest of this document. By default, Sqitch will read F in the current directory for settings. But it will also read F<~/.sqitch/sqitch.conf> for user-specific @@ -160,7 +166,16 @@ to deploy to: > createdb flipr_test Now we tell Sqitch where to send the change via a -L: +L. The examples here +use C, assuming a passwordless connection on a local +socket. If you need to specify a username and hostname, it would be more +like C. For YugabyteDB, you might +also need the port, as in C. +For CockroachDB, use C, as in +C. + +Sticking with simply C for these examples, we use the +C command to deploy the change: > sqitch deploy db:pg:flipr_test Adding registry tables to db:pg:flipr_test @@ -1204,10 +1219,6 @@ Ah, that looks a bit better. Let's have a look at the plan: %project=flipr %uri=https://github.com/sqitchers/sqitch-intro/ - %syntax-version=1.0.0 - %project=flipr - %uri=https://github.com/sqitchers/sqitch-intro/ - appschema 2013-12-30T23:19:45Z Marge N. Oโ€™Vera # Add schema for all flipr objects. users [appschema] 2013-12-30T23:49:00Z Marge N. Oโ€™Vera # Creates table to track our users. insert_user [users appschema] 2013-12-30T23:57:36Z Marge N. Oโ€™Vera # Creates a function to insert a user. @@ -1666,7 +1677,7 @@ David E. Wheeler =head1 License -Copyright (c) 2012-2021 iovation Inc., David E. Wheeler +Copyright (c) 2012-2022 iovation Inc., David E. Wheeler Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/po/App-Sqitch.pot b/po/App-Sqitch.pot index 553d3d65f..19e082b26 100644 --- a/po/App-Sqitch.pot +++ b/po/App-Sqitch.pot @@ -1,14 +1,14 @@ # Sqitch Localization Messages -# Copyright (c) 2012-2021 "iovation Inc., David E. Wheeler" +# Copyright (c) 2012-2022 "iovation Inc., David E. Wheeler" # This file is distributed under the same license as the App-Sqitch package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" -"Project-Id-Version: App-Sqitch v1.2.1\n" +"Project-Id-Version: App-Sqitch v1.3.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-11-14 19:00-0500\n" +"POT-Creation-Date: 2022-08-12 16:04-0400\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -28,118 +28,125 @@ msgid "" "you@host.com" msgstr "" -#: lib/App/Sqitch.pm:282 +#: lib/App/Sqitch.pm:280 #, perl-brace-format msgid "Cannot change to directory {directory}: {error}" msgstr "" -#: lib/App/Sqitch.pm:316 lib/App/Sqitch/Command.pm:112 +#: lib/App/Sqitch.pm:314 lib/App/Sqitch/Command.pm:113 #, perl-brace-format msgid "\"{command}\" is not a valid command" msgstr "" -#: lib/App/Sqitch.pm:410 +#: lib/App/Sqitch.pm:408 msgid "" "Sqitch seems to be unattended and there is no default value for this question" msgstr "" -#: lib/App/Sqitch.pm:429 +#: lib/App/Sqitch.pm:427 msgctxt "Confirm prompt answer yes" msgid "Yes" msgstr "" -#: lib/App/Sqitch.pm:430 +#: lib/App/Sqitch.pm:428 msgctxt "Confirm prompt answer no" msgid "No" msgstr "" -#: lib/App/Sqitch.pm:439 +#: lib/App/Sqitch.pm:437 msgid "Please answer \"y\" or \"n\"." msgstr "" -#: lib/App/Sqitch.pm:442 +#: lib/App/Sqitch.pm:440 msgid "No valid answer after 3 attempts; aborting" msgstr "" -#: lib/App/Sqitch.pm:463 lib/App/Sqitch.pm:470 +#: lib/App/Sqitch.pm:461 lib/App/Sqitch.pm:468 #, perl-brace-format msgid "Cannot exec {command}: {error}" msgstr "" -#: lib/App/Sqitch.pm:486 +#: lib/App/Sqitch.pm:484 #, perl-brace-format msgid "Error closing pipe to {command}: {error}" msgstr "" -#: lib/App/Sqitch.pm:490 lib/App/Sqitch/Engine/oracle.pm:758 +#: lib/App/Sqitch.pm:488 lib/App/Sqitch/Engine/oracle.pm:761 #, perl-brace-format msgid "{command} unexpectedly returned exit value {exitval}" msgstr "" -#: lib/App/Sqitch/Command.pm:282 +#: lib/App/Sqitch/Command.pm:283 #, perl-brace-format msgid "Unknown argument \"{arg}\"" msgid_plural "Unknown arguments: {arg}" msgstr[0] "" msgstr[1] "" -#: lib/App/Sqitch/Command.pm:294 +#: lib/App/Sqitch/Command.pm:295 msgid "Cannot specify both --all and engine, target, or plan arugments" msgstr "" -#: lib/App/Sqitch/Command/add.pm:103 +#: lib/App/Sqitch/Command.pm:315 lib/App/Sqitch/Command/add.pm:410 +#: lib/App/Sqitch/Command/init.pm:203 +#: lib/App/Sqitch/Role/TargetConfigCommand.pm:250 +#: lib/App/Sqitch/Role/TargetConfigCommand.pm:337 +#, perl-brace-format +msgid "Created {file}" +msgstr "" + +#: lib/App/Sqitch/Command.pm:322 lib/App/Sqitch/Role/TargetConfigCommand.pm:256 +#, perl-brace-format +msgid "Error creating {path}: {error}" +msgstr "" + +#: lib/App/Sqitch/Command/add.pm:102 #, perl-brace-format msgid "Template {template} does not exist" msgstr "" -#: lib/App/Sqitch/Command/add.pm:108 +#: lib/App/Sqitch/Command/add.pm:107 #, perl-brace-format msgid "Template {template} is not a file" msgstr "" -#: lib/App/Sqitch/Command/add.pm:146 +#: lib/App/Sqitch/Command/add.pm:145 #, perl-brace-format msgid "Cannot find {script} template" msgstr "" -#: lib/App/Sqitch/Command/add.pm:225 +#: lib/App/Sqitch/Command/add.pm:224 #, perl-brace-format msgid "Directory \"{dir}\" does not exist" msgstr "" -#: lib/App/Sqitch/Command/add.pm:230 +#: lib/App/Sqitch/Command/add.pm:229 #, perl-brace-format msgid "\"{dir}\" is not a directory" msgstr "" -#: lib/App/Sqitch/Command/add.pm:283 +#: lib/App/Sqitch/Command/add.pm:282 #, perl-brace-format msgid "" "Name \"{name}\" identifies a target; use \"--change {name}\" to use it for " "the change name" msgstr "" -#: lib/App/Sqitch/Command/add.pm:331 +#: lib/App/Sqitch/Command/add.pm:330 msgid "add" msgstr "" -#: lib/App/Sqitch/Command/add.pm:349 +#: lib/App/Sqitch/Command/add.pm:348 #, perl-brace-format msgid "Added \"{change}\" to {file}" msgstr "" -#: lib/App/Sqitch/Command/add.pm:368 +#: lib/App/Sqitch/Command/add.pm:367 #, perl-brace-format msgid "Skipped {file}: already exists" msgstr "" -#: lib/App/Sqitch/Command/add.pm:379 lib/App/Sqitch/Command/bundle.pm:141 -#: lib/App/Sqitch/Role/TargetConfigCommand.pm:256 -#, perl-brace-format -msgid "Error creating {path}: {error}" -msgstr "" - -#: lib/App/Sqitch/Command/add.pm:396 lib/App/Sqitch/Command/add.pm:426 +#: lib/App/Sqitch/Command/add.pm:386 lib/App/Sqitch/Command/add.pm:416 #: lib/App/Sqitch/Plan.pm:134 lib/App/Sqitch/Plan.pm:591 #: lib/App/Sqitch/Plan.pm:972 lib/App/Sqitch/Plan/Line.pm:107 #: lib/App/Sqitch/Role/TargetConfigCommand.pm:321 @@ -147,25 +154,17 @@ msgstr "" msgid "Cannot open {file}: {error}" msgstr "" -#: lib/App/Sqitch/Command/add.pm:404 +#: lib/App/Sqitch/Command/add.pm:394 #, perl-brace-format msgid "Error executing {template}: {error}" msgstr "" -#: lib/App/Sqitch/Command/add.pm:416 +#: lib/App/Sqitch/Command/add.pm:406 #: lib/App/Sqitch/Role/TargetConfigCommand.pm:332 #, perl-brace-format msgid "Error closing {file}: {error}" msgstr "" -#: lib/App/Sqitch/Command/add.pm:420 lib/App/Sqitch/Command/bundle.pm:134 -#: lib/App/Sqitch/Command/init.pm:203 -#: lib/App/Sqitch/Role/TargetConfigCommand.pm:250 -#: lib/App/Sqitch/Role/TargetConfigCommand.pm:337 -#, perl-brace-format -msgid "Created {file}" -msgstr "" - #: lib/App/Sqitch/Command/bundle.pm:99 msgid "" "Use of --to or --from to bundle multiple targets is not recommended.\n" @@ -181,41 +180,41 @@ msgstr "" msgid "Bundling into {dir}" msgstr "" -#: lib/App/Sqitch/Command/bundle.pm:152 +#: lib/App/Sqitch/Command/bundle.pm:136 #, perl-brace-format msgid "Cannot copy {file}: does not exist" msgstr "" -#: lib/App/Sqitch/Command/bundle.pm:165 +#: lib/App/Sqitch/Command/bundle.pm:149 #, perl-brace-format msgid "Copying {source} -> {dest}" msgstr "" -#: lib/App/Sqitch/Command/bundle.pm:172 +#: lib/App/Sqitch/Command/bundle.pm:156 #, perl-brace-format msgid "Cannot copy \"{source}\" to \"{dest}\": {error}" msgstr "" -#: lib/App/Sqitch/Command/bundle.pm:182 +#: lib/App/Sqitch/Command/bundle.pm:166 msgid "Writing config" msgstr "" -#: lib/App/Sqitch/Command/bundle.pm:193 +#: lib/App/Sqitch/Command/bundle.pm:177 msgid "Writing plan" msgstr "" -#: lib/App/Sqitch/Command/bundle.pm:202 +#: lib/App/Sqitch/Command/bundle.pm:186 #, perl-brace-format msgid "Writing plan from {from} to {to}" msgstr "" -#: lib/App/Sqitch/Command/bundle.pm:222 lib/App/Sqitch/Command/bundle.pm:229 +#: lib/App/Sqitch/Command/bundle.pm:206 lib/App/Sqitch/Command/bundle.pm:213 #: lib/App/Sqitch/Plan.pm:944 lib/App/Sqitch/Plan.pm:953 #, perl-brace-format msgid "Cannot find change {change}" msgstr "" -#: lib/App/Sqitch/Command/bundle.pm:233 +#: lib/App/Sqitch/Command/bundle.pm:217 msgid "Writing scripts" msgstr "" @@ -367,34 +366,6 @@ msgstr "" msgid "No Variables" msgstr "" -#: lib/App/Sqitch/Command/engine.pm:253 -#, perl-brace-format -msgid "Loading {file}" -msgstr "" - -#: lib/App/Sqitch/Command/engine.pm:266 -#, perl-brace-format -msgid "" -"Deprecated {section} found in {file}; to remove it, run\n" -" {sqitch} config --file {file} --remove-section {section}" -msgstr "" - -#: lib/App/Sqitch/Command/engine.pm:278 -msgid " - No engines to update" -msgstr "" - -#: lib/App/Sqitch/Command/engine.pm:285 -#, perl-brace-format -msgid "Cannot update {file}. Please make it writable" -msgstr "" - -#: lib/App/Sqitch/Command/engine.pm:341 -#, perl-brace-format -msgid "" -"Migrated {old} to {new}; To remove {old}, run\n" -" {sqitch} config --file {file} --remove-section {old}" -msgstr "" - #: lib/App/Sqitch/Command/help.pm:46 #, perl-brace-format msgid "No manual entry for {command}" @@ -433,7 +404,7 @@ msgstr "" msgid "Unknown plan format \"{format}\"" msgstr "" -#: lib/App/Sqitch/Command/plan.pm:199 lib/App/Sqitch/Command/upgrade.pm:40 +#: lib/App/Sqitch/Command/plan.pm:199 lib/App/Sqitch/Command/upgrade.pm:39 #, perl-brace-format msgid "Too many targets specified; using {target}" msgstr "" @@ -536,8 +507,8 @@ msgstr "" msgid ", " msgstr "" -#: lib/App/Sqitch/Command/status.pm:138 lib/App/Sqitch/Engine.pm:381 -#: lib/App/Sqitch/Engine.pm:1214 +#: lib/App/Sqitch/Command/status.pm:138 lib/App/Sqitch/Engine.pm:384 +#: lib/App/Sqitch/Engine.pm:1216 msgid "No changes deployed" msgstr "" @@ -603,11 +574,11 @@ msgstr "" msgid "Make sure you are connected to the proper database for this project." msgstr "" -#: lib/App/Sqitch/Command/status.pm:308 lib/App/Sqitch/Engine.pm:216 +#: lib/App/Sqitch/Command/status.pm:308 lib/App/Sqitch/Engine.pm:217 msgid "Nothing to deploy (up-to-date)" msgstr "" -#: lib/App/Sqitch/Command/status.pm:311 lib/App/Sqitch/Engine.pm:536 +#: lib/App/Sqitch/Command/status.pm:311 lib/App/Sqitch/Engine.pm:539 msgid "Undeployed change:" msgid_plural "Undeployed changes:" msgstr[0] "" @@ -648,12 +619,12 @@ msgstr "" msgid "URI" msgstr "" -#: lib/App/Sqitch/Command/upgrade.pm:48 +#: lib/App/Sqitch/Command/upgrade.pm:47 #, perl-brace-format msgid "Upgrading registry {registry} to version {version}" msgstr "" -#: lib/App/Sqitch/Command/upgrade.pm:55 +#: lib/App/Sqitch/Command/upgrade.pm:54 #, perl-brace-format msgid "Registry {registry} is up-to-date at version {version}" msgstr "" @@ -682,187 +653,187 @@ msgstr "" msgid "{driver} required to manage {engine}" msgstr "" -#: lib/App/Sqitch/Engine.pm:196 +#: lib/App/Sqitch/Engine.pm:197 msgid "Nothing to deploy (empty plan)" msgstr "" -#: lib/App/Sqitch/Engine.pm:200 lib/App/Sqitch/Engine.pm:294 +#: lib/App/Sqitch/Engine.pm:201 lib/App/Sqitch/Engine.pm:297 #: lib/App/Sqitch/Plan.pm:738 lib/App/Sqitch/Plan/ChangeList.pm:121 #, perl-brace-format msgid "Unknown change: \"{change}\"" msgstr "" -#: lib/App/Sqitch/Engine.pm:207 +#: lib/App/Sqitch/Engine.pm:208 #, perl-brace-format msgid "Nothing to deploy (already at \"{change}\")" msgstr "" -#: lib/App/Sqitch/Engine.pm:225 +#: lib/App/Sqitch/Engine.pm:226 #, perl-brace-format msgid "Adding registry tables to {destination}" msgstr "" -#: lib/App/Sqitch/Engine.pm:234 +#: lib/App/Sqitch/Engine.pm:235 msgid "Cannot deploy to an earlier change; use \"revert\" instead" msgstr "" -#: lib/App/Sqitch/Engine.pm:242 +#: lib/App/Sqitch/Engine.pm:243 #, perl-brace-format msgid "Deploying changes through {change} to {destination}" msgstr "" -#: lib/App/Sqitch/Engine.pm:246 +#: lib/App/Sqitch/Engine.pm:247 #, perl-brace-format msgid "Deploying changes to {destination}" msgstr "" -#: lib/App/Sqitch/Engine.pm:259 +#: lib/App/Sqitch/Engine.pm:260 #, perl-brace-format msgid "Unknown deployment mode: \"{mode}\"" msgstr "" -#: lib/App/Sqitch/Engine.pm:288 +#: lib/App/Sqitch/Engine.pm:291 #, perl-brace-format msgid "Change not deployed: \"{change}\"" msgstr "" -#: lib/App/Sqitch/Engine.pm:303 +#: lib/App/Sqitch/Engine.pm:306 #, perl-brace-format msgid "No changes deployed since: \"{change}\"" msgstr "" -#: lib/App/Sqitch/Engine.pm:311 +#: lib/App/Sqitch/Engine.pm:314 #, perl-brace-format msgid "Reverting changes to {change} from {destination}" msgstr "" -#: lib/App/Sqitch/Engine.pm:318 lib/App/Sqitch/Engine.pm:341 +#: lib/App/Sqitch/Engine.pm:321 lib/App/Sqitch/Engine.pm:344 msgid "Nothing reverted" msgstr "" -#: lib/App/Sqitch/Engine.pm:321 +#: lib/App/Sqitch/Engine.pm:324 #, perl-brace-format msgid "Revert changes to {change} from {destination}?" msgstr "" -#: lib/App/Sqitch/Engine.pm:329 +#: lib/App/Sqitch/Engine.pm:332 msgid "Nothing to revert (nothing deployed)" msgstr "" -#: lib/App/Sqitch/Engine.pm:335 +#: lib/App/Sqitch/Engine.pm:338 #, perl-brace-format msgid "Reverting all changes from {destination}" msgstr "" -#: lib/App/Sqitch/Engine.pm:344 +#: lib/App/Sqitch/Engine.pm:347 #, perl-brace-format msgid "Revert all changes from {destination}?" msgstr "" -#: lib/App/Sqitch/Engine.pm:375 +#: lib/App/Sqitch/Engine.pm:378 #, perl-brace-format msgid "Verifying {destination}" msgstr "" -#: lib/App/Sqitch/Engine.pm:382 +#: lib/App/Sqitch/Engine.pm:385 msgid "Nothing to verify (no planned or deployed changes)" msgstr "" -#: lib/App/Sqitch/Engine.pm:389 +#: lib/App/Sqitch/Engine.pm:392 msgid "There are deployed changes, but none planned!" msgstr "" -#: lib/App/Sqitch/Engine.pm:402 +#: lib/App/Sqitch/Engine.pm:405 msgid "Verify Summary Report" msgstr "" -#: lib/App/Sqitch/Engine.pm:405 +#: lib/App/Sqitch/Engine.pm:408 #, perl-brace-format msgid "Changes: {number}" msgstr "" -#: lib/App/Sqitch/Engine.pm:406 +#: lib/App/Sqitch/Engine.pm:409 #, perl-brace-format msgid "Errors: {number}" msgstr "" -#: lib/App/Sqitch/Engine.pm:407 +#: lib/App/Sqitch/Engine.pm:410 msgid "Verify failed" msgstr "" -#: lib/App/Sqitch/Engine.pm:412 +#: lib/App/Sqitch/Engine.pm:415 msgid "Verify successful" msgstr "" -#: lib/App/Sqitch/Engine.pm:441 +#: lib/App/Sqitch/Engine.pm:444 #, perl-brace-format msgid "Change \"{change}\" has not been deployed" msgstr "" -#: lib/App/Sqitch/Engine.pm:444 +#: lib/App/Sqitch/Engine.pm:447 #, perl-brace-format msgid "Cannot find \"{change}\" in the database or the plan" msgstr "" -#: lib/App/Sqitch/Engine.pm:451 +#: lib/App/Sqitch/Engine.pm:454 #, perl-brace-format msgid "Change \"{change}\" is deployed, but not planned" msgstr "" -#: lib/App/Sqitch/Engine.pm:495 +#: lib/App/Sqitch/Engine.pm:498 msgid "Out of order" msgstr "" -#: lib/App/Sqitch/Engine.pm:501 +#: lib/App/Sqitch/Engine.pm:504 msgid "Not present in the plan" msgstr "" -#: lib/App/Sqitch/Engine.pm:512 lib/App/Sqitch/Engine.pm:524 -#: lib/App/Sqitch/Engine.pm:993 lib/App/Sqitch/Engine.pm:1023 +#: lib/App/Sqitch/Engine.pm:515 lib/App/Sqitch/Engine.pm:527 +#: lib/App/Sqitch/Engine.pm:996 lib/App/Sqitch/Engine.pm:1026 msgid "not ok" msgstr "" -#: lib/App/Sqitch/Engine.pm:512 lib/App/Sqitch/Engine.pm:971 -#: lib/App/Sqitch/Engine.pm:1013 +#: lib/App/Sqitch/Engine.pm:515 lib/App/Sqitch/Engine.pm:974 +#: lib/App/Sqitch/Engine.pm:1016 msgid "ok" msgstr "" -#: lib/App/Sqitch/Engine.pm:526 +#: lib/App/Sqitch/Engine.pm:529 msgid "Not deployed" msgstr "" -#: lib/App/Sqitch/Engine.pm:558 +#: lib/App/Sqitch/Engine.pm:561 #, perl-brace-format msgid "Verify script \"{script}\" failed." msgstr "" -#: lib/App/Sqitch/Engine.pm:567 +#: lib/App/Sqitch/Engine.pm:570 #, perl-brace-format msgid "Verify script {file} does not exist" msgstr "" -#: lib/App/Sqitch/Engine.pm:609 +#: lib/App/Sqitch/Engine.pm:612 #, perl-brace-format msgid "Conflicts with previously deployed change: {changes}" msgid_plural "Conflicts with previously deployed changes: {changes}" msgstr[0] "" msgstr[1] "" -#: lib/App/Sqitch/Engine.pm:616 +#: lib/App/Sqitch/Engine.pm:619 #, perl-brace-format msgid "Missing required change: {changes}" msgid_plural "Missing required changes: {changes}" msgstr[0] "" msgstr[1] "" -#: lib/App/Sqitch/Engine.pm:628 +#: lib/App/Sqitch/Engine.pm:631 #, perl-brace-format msgid "Change \"{changes}\" has already been deployed" msgid_plural "Changes have already been deployed: {changes}" msgstr[0] "" msgstr[1] "" -#: lib/App/Sqitch/Engine.pm:651 +#: lib/App/Sqitch/Engine.pm:654 #, perl-brace-format msgid "Change \"{change}\" required by currently deployed change: {changes}" msgid_plural "" @@ -870,138 +841,138 @@ msgid_plural "" msgstr[0] "" msgstr[1] "" -#: lib/App/Sqitch/Engine.pm:674 +#: lib/App/Sqitch/Engine.pm:677 #, perl-brace-format msgid "Invalid dependency: {dependency}" msgstr "" -#: lib/App/Sqitch/Engine.pm:810 lib/App/Sqitch/Plan/ChangeList.pm:88 +#: lib/App/Sqitch/Engine.pm:813 lib/App/Sqitch/Plan/ChangeList.pm:88 #, perl-brace-format msgid "" "Change \"{change}\" is ambiguous. Please specify a tag-qualified change:" msgstr "" -#: lib/App/Sqitch/Engine.pm:825 +#: lib/App/Sqitch/Engine.pm:828 msgid "Change Lookup Failed" msgstr "" -#: lib/App/Sqitch/Engine.pm:846 +#: lib/App/Sqitch/Engine.pm:849 #, perl-brace-format msgid "Reverting to {change}" msgstr "" -#: lib/App/Sqitch/Engine.pm:847 +#: lib/App/Sqitch/Engine.pm:850 msgid "Reverting all changes" msgstr "" -#: lib/App/Sqitch/Engine.pm:855 +#: lib/App/Sqitch/Engine.pm:858 msgid "The schema will need to be manually repaired" msgstr "" -#: lib/App/Sqitch/Engine.pm:859 lib/App/Sqitch/Engine.pm:987 +#: lib/App/Sqitch/Engine.pm:862 lib/App/Sqitch/Engine.pm:990 msgid "Deploy failed" msgstr "" -#: lib/App/Sqitch/Engine.pm:916 +#: lib/App/Sqitch/Engine.pm:919 #, perl-brace-format msgid "Cannot find change {id} ({change}) in {file}" msgstr "" -#: lib/App/Sqitch/Engine.pm:1038 +#: lib/App/Sqitch/Engine.pm:1041 #, perl-brace-format msgid "" "Blocked by another instance of Sqitch working on {dest}; waiting {secs} " "seconds..." msgstr "" -#: lib/App/Sqitch/Engine.pm:1048 +#: lib/App/Sqitch/Engine.pm:1051 #, perl-brace-format msgid "" "Timed out waiting {secs} seconds for another instance of Sqitch to finish " "work on {dest}" msgstr "" -#: lib/App/Sqitch/Engine.pm:1108 +#: lib/App/Sqitch/Engine.pm:1110 #, perl-brace-format msgid "No registry found in {destination}. Have you ever deployed?" msgstr "" -#: lib/App/Sqitch/Engine.pm:1113 +#: lib/App/Sqitch/Engine.pm:1115 #, perl-brace-format msgid "" "Registry version is {old} but {new} is the latest known. Please upgrade " "Sqitch" msgstr "" -#: lib/App/Sqitch/Engine.pm:1119 +#: lib/App/Sqitch/Engine.pm:1121 #, perl-brace-format msgid "" "Registry is at version {old} but latest is {new}. Please run the \"upgrade\" " "command" msgstr "" -#: lib/App/Sqitch/Engine.pm:1134 +#: lib/App/Sqitch/Engine.pm:1136 #, perl-brace-format msgid "" "Registry version is {old} but {new} is the latest known. Please upgrade " "Sqitch." msgstr "" -#: lib/App/Sqitch/Engine.pm:1149 +#: lib/App/Sqitch/Engine.pm:1151 #, perl-brace-format msgid "Cannot upgrade to {version}: Cannot find upgrade script \"{file}\"" msgstr "" -#: lib/App/Sqitch/Engine.pm:1156 +#: lib/App/Sqitch/Engine.pm:1158 #, perl-brace-format msgid "Upgrading the Sqitch registry from {old} to {new}" msgstr "" -#: lib/App/Sqitch/Engine.pm:1163 +#: lib/App/Sqitch/Engine.pm:1165 #, perl-brace-format msgid "From {old} to {new}" msgstr "" -#: lib/App/Sqitch/Engine.pm:1208 +#: lib/App/Sqitch/Engine.pm:1210 #, perl-brace-format msgid "Checking {destination}" msgstr "" -#: lib/App/Sqitch/Engine.pm:1215 +#: lib/App/Sqitch/Engine.pm:1217 msgid "Nothing to check (no planned or deployed changes)" msgstr "" -#: lib/App/Sqitch/Engine.pm:1228 +#: lib/App/Sqitch/Engine.pm:1230 #, perl-brace-format msgid "Script signatures diverge at change {change}" msgstr "" -#: lib/App/Sqitch/Engine.pm:1236 +#: lib/App/Sqitch/Engine.pm:1238 #, perl-brace-format msgid "Failed one check" msgid_plural "Failed {count} checks" msgstr[0] "" msgstr[1] "" -#: lib/App/Sqitch/Engine.pm:1244 +#: lib/App/Sqitch/Engine.pm:1246 msgid "Check successful" msgstr "" -#: lib/App/Sqitch/Engine/exasol.pm:324 lib/App/Sqitch/Engine/oracle.pm:455 +#: lib/App/Sqitch/Engine/exasol.pm:324 lib/App/Sqitch/Engine/oracle.pm:458 msgid "Sqitch already initialized" msgstr "" -#: lib/App/Sqitch/Engine/exasol.pm:392 lib/App/Sqitch/Engine/oracle.pm:585 +#: lib/App/Sqitch/Engine/exasol.pm:392 lib/App/Sqitch/Engine/oracle.pm:588 #, perl-brace-format msgid "Cannot remove {file}: {error}" msgstr "" -#: lib/App/Sqitch/Engine/exasol.pm:401 lib/App/Sqitch/Engine/oracle.pm:594 +#: lib/App/Sqitch/Engine/exasol.pm:401 lib/App/Sqitch/Engine/oracle.pm:597 #, perl-brace-format msgid "Cannot copy {file} to {alias}: {error}" msgstr "" -#: lib/App/Sqitch/Engine/exasol.pm:410 lib/App/Sqitch/Engine/oracle.pm:603 +#: lib/App/Sqitch/Engine/exasol.pm:410 lib/App/Sqitch/Engine/oracle.pm:606 #, perl-brace-format msgid "Cannot symlink {file} to {alias}: {error}" msgstr "" @@ -1011,34 +982,34 @@ msgstr "" msgid "{command} unexpectedly failed; exit value = {exitval}" msgstr "" -#: lib/App/Sqitch/Engine/firebird.pm:207 lib/App/Sqitch/Engine/mysql.pm:274 +#: lib/App/Sqitch/Engine/firebird.pm:208 lib/App/Sqitch/Engine/mysql.pm:281 #: lib/App/Sqitch/Engine/sqlite.pm:158 #, perl-brace-format msgid "Sqitch database {database} already initialized" msgstr "" -#: lib/App/Sqitch/Engine/firebird.pm:226 +#: lib/App/Sqitch/Engine/firebird.pm:227 #, perl-brace-format msgid "Cannot create database {database}: {error}" msgstr "" -#: lib/App/Sqitch/Engine/firebird.pm:240 lib/App/Sqitch/Engine/sqlite.pm:127 +#: lib/App/Sqitch/Engine/firebird.pm:241 lib/App/Sqitch/Engine/sqlite.pm:127 #, perl-brace-format msgid "Database name missing in URI {uri}" msgstr "" -#: lib/App/Sqitch/Engine/firebird.pm:881 lib/App/Sqitch/Engine/firebird.pm:898 -#: lib/App/Sqitch/Engine/firebird.pm:909 +#: lib/App/Sqitch/Engine/firebird.pm:889 lib/App/Sqitch/Engine/firebird.pm:907 +#: lib/App/Sqitch/Engine/firebird.pm:918 #, perl-brace-format msgid "Cannot dup STDERR: {error}" msgstr "" -#: lib/App/Sqitch/Engine/firebird.pm:885 +#: lib/App/Sqitch/Engine/firebird.pm:893 #, perl-brace-format msgid "Cannot reirect STDERR: {error}" msgstr "" -#: lib/App/Sqitch/Engine/firebird.pm:912 +#: lib/App/Sqitch/Engine/firebird.pm:921 msgid "" "Unable to locate Firebird ISQL; set \"engine.firebird.client\" via sqitch " "config" @@ -1055,17 +1026,17 @@ msgstr "" msgid "Database name missing in URI \"{uri}\"" msgstr "" -#: lib/App/Sqitch/Engine/pg.pm:189 lib/App/Sqitch/Engine/snowflake.pm:300 +#: lib/App/Sqitch/Engine/pg.pm:212 lib/App/Sqitch/Engine/snowflake.pm:300 #: lib/App/Sqitch/Engine/vertica.pm:142 #, perl-brace-format msgid "Sqitch schema \"{schema}\" already exists" msgstr "" -#: lib/App/Sqitch/Engine/pg.pm:412 +#: lib/App/Sqitch/Engine/pg.pm:452 msgid "Sqitch registry not initialized" msgstr "" -#: lib/App/Sqitch/Engine/pg.pm:413 +#: lib/App/Sqitch/Engine/pg.pm:453 msgid "" "Because the \"changes\" table does not exist, Sqitch will now initialize the " "database to create its registry tables." diff --git a/po/de_DE.po b/po/de_DE.po index ce2c43d6d..b3c22dfc5 100644 --- a/po/de_DE.po +++ b/po/de_DE.po @@ -1,5 +1,5 @@ # Sqitch German Localization Messages -# Copyright (c) 2012-2021 iovation Inc., David E. Wheeler +# Copyright (c) 2012-2022 iovation Inc., David E. Wheeler # This file is distributed under the same license as the App-Sqitch package. # Thomas Iguchi , 2019. # @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: Sqitch 0.932\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-11-14 19:00-0500\n" +"POT-Creation-Date: 2022-08-12 16:04-0400\n" "PO-Revision-Date: 2012-08-31 17:15-0700\n" "Last-Translator: Thomas Iguchi \n" "Language-Team: German \n" @@ -33,94 +33,107 @@ msgstr "" "Kann deine E-Mail-Adresse nicht finden. Fรผhre den folgenden Befehl aus: " "sqitch config --user user.email dein.name@domain.com" -#: lib/App/Sqitch.pm:282 +#: lib/App/Sqitch.pm:280 #, perl-brace-format msgid "Cannot change to directory {directory}: {error}" msgstr "Konnte nicht in das folgende Verzeichnis wechseln {directory}: {error}" -#: lib/App/Sqitch.pm:316 lib/App/Sqitch/Command.pm:112 +#: lib/App/Sqitch.pm:314 lib/App/Sqitch/Command.pm:113 #, perl-brace-format msgid "\"{command}\" is not a valid command" msgstr "\"{command}\" ist ein ungรผltiger Befehl" -#: lib/App/Sqitch.pm:410 +#: lib/App/Sqitch.pm:408 msgid "" "Sqitch seems to be unattended and there is no default value for this question" msgstr "" "Sqitch scheint unbeaufsichtigt ausgefรผhrt zu werden, und es steht leider " "keine Standardantwort fรผr diese Frage zur Verfรผgung" -#: lib/App/Sqitch.pm:429 +#: lib/App/Sqitch.pm:427 msgctxt "Confirm prompt answer yes" msgid "Yes" msgstr "Ja" -#: lib/App/Sqitch.pm:430 +#: lib/App/Sqitch.pm:428 msgctxt "Confirm prompt answer no" msgid "No" msgstr "Nein" -#: lib/App/Sqitch.pm:439 +#: lib/App/Sqitch.pm:437 msgid "Please answer \"y\" or \"n\"." msgstr "Bitte antworte mit \"j\" oder \"n\"" -#: lib/App/Sqitch.pm:442 +#: lib/App/Sqitch.pm:440 msgid "No valid answer after 3 attempts; aborting" msgstr "Drei ungรผltige Antworten in Folge. Breche ab" -#: lib/App/Sqitch.pm:463 lib/App/Sqitch.pm:470 +#: lib/App/Sqitch.pm:461 lib/App/Sqitch.pm:468 #, perl-brace-format msgid "Cannot exec {command}: {error}" msgstr "Konnte den Befehl {command} nicht ausfรผhren: {error}" -#: lib/App/Sqitch.pm:486 +#: lib/App/Sqitch.pm:484 #, perl-brace-format msgid "Error closing pipe to {command}: {error}" msgstr "Fehler beim SchlieรŸen der Pipeline nach {command}: {error}" -#: lib/App/Sqitch.pm:490 lib/App/Sqitch/Engine/oracle.pm:758 +#: lib/App/Sqitch.pm:488 lib/App/Sqitch/Engine/oracle.pm:761 #, perl-brace-format msgid "{command} unexpectedly returned exit value {exitval}" msgstr "{command} brach unerwartet ab mit Rรผckgabewert {exitval}" -#: lib/App/Sqitch/Command.pm:282 +#: lib/App/Sqitch/Command.pm:283 #, perl-brace-format msgid "Unknown argument \"{arg}\"" msgid_plural "Unknown arguments: {arg}" msgstr[0] "Unbekanntes Argument \"{arg}\"" msgstr[1] "Unbekannte Argumente: \"{arg}\"" -#: lib/App/Sqitch/Command.pm:294 +#: lib/App/Sqitch/Command.pm:295 msgid "Cannot specify both --all and engine, target, or plan arugments" msgstr "" "Das Argument --all kann nicht mit engine, target oder plan kombiniert werden" -#: lib/App/Sqitch/Command/add.pm:103 +#: lib/App/Sqitch/Command.pm:315 lib/App/Sqitch/Command/add.pm:410 +#: lib/App/Sqitch/Command/init.pm:203 +#: lib/App/Sqitch/Role/TargetConfigCommand.pm:250 +#: lib/App/Sqitch/Role/TargetConfigCommand.pm:337 +#, perl-brace-format +msgid "Created {file}" +msgstr "{file} erstellt" + +#: lib/App/Sqitch/Command.pm:322 lib/App/Sqitch/Role/TargetConfigCommand.pm:256 +#, perl-brace-format +msgid "Error creating {path}: {error}" +msgstr "Fehler beim Erstellen von {path}: {error}" + +#: lib/App/Sqitch/Command/add.pm:102 #, perl-brace-format msgid "Template {template} does not exist" msgstr "Vorlage {template} existiert nicht" -#: lib/App/Sqitch/Command/add.pm:108 +#: lib/App/Sqitch/Command/add.pm:107 #, perl-brace-format msgid "Template {template} is not a file" msgstr "Vorlage {template} ist keine Datei" -#: lib/App/Sqitch/Command/add.pm:146 +#: lib/App/Sqitch/Command/add.pm:145 #, perl-brace-format msgid "Cannot find {script} template" msgstr "Kann Vorlage {script} nicht finden" -#: lib/App/Sqitch/Command/add.pm:225 +#: lib/App/Sqitch/Command/add.pm:224 #, perl-brace-format msgid "Directory \"{dir}\" does not exist" msgstr "Verzeichnis \"{dir}\" existert nicht" -#: lib/App/Sqitch/Command/add.pm:230 +#: lib/App/Sqitch/Command/add.pm:229 #, perl-brace-format msgid "\"{dir}\" is not a directory" msgstr "\"{dir}\" ist kein Verzeichnis" -#: lib/App/Sqitch/Command/add.pm:283 +#: lib/App/Sqitch/Command/add.pm:282 #, perl-brace-format msgid "" "Name \"{name}\" identifies a target; use \"--change {name}\" to use it for " @@ -129,27 +142,21 @@ msgstr "" "Der Name \"{name}\" bezeichnet ein Ziel. Verwende \"--change {name}\" um ihn " "als ร„nderung anzugeben" -#: lib/App/Sqitch/Command/add.pm:331 +#: lib/App/Sqitch/Command/add.pm:330 msgid "add" msgstr "hinzufรผgen" -#: lib/App/Sqitch/Command/add.pm:349 +#: lib/App/Sqitch/Command/add.pm:348 #, perl-brace-format msgid "Added \"{change}\" to {file}" msgstr "\"{change}\" der Datei {file} hinzugefรผgt" -#: lib/App/Sqitch/Command/add.pm:368 +#: lib/App/Sqitch/Command/add.pm:367 #, perl-brace-format msgid "Skipped {file}: already exists" msgstr "รœberspringe {file}: existiert bereits" -#: lib/App/Sqitch/Command/add.pm:379 lib/App/Sqitch/Command/bundle.pm:141 -#: lib/App/Sqitch/Role/TargetConfigCommand.pm:256 -#, perl-brace-format -msgid "Error creating {path}: {error}" -msgstr "Fehler beim Erstellen von {path}: {error}" - -#: lib/App/Sqitch/Command/add.pm:396 lib/App/Sqitch/Command/add.pm:426 +#: lib/App/Sqitch/Command/add.pm:386 lib/App/Sqitch/Command/add.pm:416 #: lib/App/Sqitch/Plan.pm:134 lib/App/Sqitch/Plan.pm:591 #: lib/App/Sqitch/Plan.pm:972 lib/App/Sqitch/Plan/Line.pm:107 #: lib/App/Sqitch/Role/TargetConfigCommand.pm:321 @@ -157,25 +164,17 @@ msgstr "Fehler beim Erstellen von {path}: {error}" msgid "Cannot open {file}: {error}" msgstr "Kann {file} nicht รถffnen: {error}" -#: lib/App/Sqitch/Command/add.pm:404 +#: lib/App/Sqitch/Command/add.pm:394 #, perl-brace-format msgid "Error executing {template}: {error}" msgstr "Fehler beim Ausfรผhren von {template}: {error}" -#: lib/App/Sqitch/Command/add.pm:416 +#: lib/App/Sqitch/Command/add.pm:406 #: lib/App/Sqitch/Role/TargetConfigCommand.pm:332 #, perl-brace-format msgid "Error closing {file}: {error}" msgstr "Fehler beim SchlieรŸen von {file}: {error}" -#: lib/App/Sqitch/Command/add.pm:420 lib/App/Sqitch/Command/bundle.pm:134 -#: lib/App/Sqitch/Command/init.pm:203 -#: lib/App/Sqitch/Role/TargetConfigCommand.pm:250 -#: lib/App/Sqitch/Role/TargetConfigCommand.pm:337 -#, perl-brace-format -msgid "Created {file}" -msgstr "{file} erstellt" - #: lib/App/Sqitch/Command/bundle.pm:99 msgid "" "Use of --to or --from to bundle multiple targets is not recommended.\n" @@ -196,41 +195,41 @@ msgstr "" msgid "Bundling into {dir}" msgstr "Packe ins Verzeichnis {dir}" -#: lib/App/Sqitch/Command/bundle.pm:152 +#: lib/App/Sqitch/Command/bundle.pm:136 #, perl-brace-format msgid "Cannot copy {file}: does not exist" msgstr "Kann {file} nicht kopieren: Datei existiert nicht" -#: lib/App/Sqitch/Command/bundle.pm:165 +#: lib/App/Sqitch/Command/bundle.pm:149 #, perl-brace-format msgid "Copying {source} -> {dest}" msgstr "Kopiere {source} -> {dest}" -#: lib/App/Sqitch/Command/bundle.pm:172 +#: lib/App/Sqitch/Command/bundle.pm:156 #, perl-brace-format msgid "Cannot copy \"{source}\" to \"{dest}\": {error}" msgstr "Kann nicht von \"{source}\" nach \"{dest}\" kopieren: {error}" -#: lib/App/Sqitch/Command/bundle.pm:182 +#: lib/App/Sqitch/Command/bundle.pm:166 msgid "Writing config" msgstr "Schreibe Konfiguration" -#: lib/App/Sqitch/Command/bundle.pm:193 +#: lib/App/Sqitch/Command/bundle.pm:177 msgid "Writing plan" msgstr "Schreibe Plan" -#: lib/App/Sqitch/Command/bundle.pm:202 +#: lib/App/Sqitch/Command/bundle.pm:186 #, perl-brace-format msgid "Writing plan from {from} to {to}" msgstr "Schreibe Plan von {from} nach {to}" -#: lib/App/Sqitch/Command/bundle.pm:222 lib/App/Sqitch/Command/bundle.pm:229 +#: lib/App/Sqitch/Command/bundle.pm:206 lib/App/Sqitch/Command/bundle.pm:213 #: lib/App/Sqitch/Plan.pm:944 lib/App/Sqitch/Plan.pm:953 #, perl-brace-format msgid "Cannot find change {change}" msgstr "Kann ร„nderung {change} nicht finden" -#: lib/App/Sqitch/Command/bundle.pm:233 +#: lib/App/Sqitch/Command/bundle.pm:217 msgid "Writing scripts" msgstr "Schreibe Skripte" @@ -389,40 +388,6 @@ msgstr "Variablen" msgid "No Variables" msgstr "Keine Variablen" -#: lib/App/Sqitch/Command/engine.pm:253 -#, perl-brace-format -msgid "Loading {file}" -msgstr "Lade {file}" - -#: lib/App/Sqitch/Command/engine.pm:266 -#, perl-brace-format -msgid "" -"Deprecated {section} found in {file}; to remove it, run\n" -" {sqitch} config --file {file} --remove-section {section}" -msgstr "" -"Veraltete Sektion {section} gefunden in {file}. Zum Entfernen fรผhre den " -"folgenden Befehl aus:\n" -" {sqitch} config --file {file} --remove-section {section}" - -#: lib/App/Sqitch/Command/engine.pm:278 -msgid " - No engines to update" -msgstr " - Keine Engines zum Aktualisieren verfรผgbar" - -#: lib/App/Sqitch/Command/engine.pm:285 -#, perl-brace-format -msgid "Cannot update {file}. Please make it writable" -msgstr "Kann {file} nicht aktualisieren. Bitte mache die Datei schreibbar" - -#: lib/App/Sqitch/Command/engine.pm:341 -#, perl-brace-format -msgid "" -"Migrated {old} to {new}; To remove {old}, run\n" -" {sqitch} config --file {file} --remove-section {old}" -msgstr "" -"{old} nach {new} migriert. Um {old} zu entfernen, fรผhre bitte den folgenden " -"Befehl aus:\n" -" {sqitch} config --file {file} --remove-section {old}" - #: lib/App/Sqitch/Command/help.pm:46 #, perl-brace-format msgid "No manual entry for {command}" @@ -465,7 +430,7 @@ msgstr "Auf Datenbank {db}" msgid "Unknown plan format \"{format}\"" msgstr "Unbekanntes Planformat \"{format}\"" -#: lib/App/Sqitch/Command/plan.pm:199 lib/App/Sqitch/Command/upgrade.pm:40 +#: lib/App/Sqitch/Command/plan.pm:199 lib/App/Sqitch/Command/upgrade.pm:39 #, perl-brace-format msgid "Too many targets specified; using {target}" msgstr "Zu viele Ziele angegeben. Verwende {target}" @@ -569,8 +534,8 @@ msgstr "Verwende --project um ein bestimmtes Projekt anzugeben: {projects}" msgid ", " msgstr ", " -#: lib/App/Sqitch/Command/status.pm:138 lib/App/Sqitch/Engine.pm:381 -#: lib/App/Sqitch/Engine.pm:1214 +#: lib/App/Sqitch/Command/status.pm:138 lib/App/Sqitch/Engine.pm:384 +#: lib/App/Sqitch/Engine.pm:1216 msgid "No changes deployed" msgstr "Keine ร„nderungen angewendet" @@ -640,11 +605,11 @@ msgstr "" "Bitte stelle sicher dass du mit der korrekten Datenbank fรผr dieses Projekt " "verbunden bist." -#: lib/App/Sqitch/Command/status.pm:308 lib/App/Sqitch/Engine.pm:216 +#: lib/App/Sqitch/Command/status.pm:308 lib/App/Sqitch/Engine.pm:217 msgid "Nothing to deploy (up-to-date)" msgstr "Nichts anzuwenden (auf neuestem Stand)" -#: lib/App/Sqitch/Command/status.pm:311 lib/App/Sqitch/Engine.pm:536 +#: lib/App/Sqitch/Command/status.pm:311 lib/App/Sqitch/Engine.pm:539 msgid "Undeployed change:" msgid_plural "Undeployed changes:" msgstr[0] "Rรผckgรคngig gemachte ร„nderung:" @@ -691,12 +656,12 @@ msgstr "" msgid "URI" msgstr "URI" -#: lib/App/Sqitch/Command/upgrade.pm:48 +#: lib/App/Sqitch/Command/upgrade.pm:47 #, perl-brace-format msgid "Upgrading registry {registry} to version {version}" msgstr "Aktualisiere Registry {registry} nach Version {version}" -#: lib/App/Sqitch/Command/upgrade.pm:55 +#: lib/App/Sqitch/Command/upgrade.pm:54 #, perl-brace-format msgid "Registry {registry} is up-to-date at version {version}" msgstr "Registry {registry} auf Version {version} aktualisiert" @@ -727,189 +692,189 @@ msgstr "" msgid "{driver} required to manage {engine}" msgstr "{driver} benรถtigt, um {engine} zu benutzen" -#: lib/App/Sqitch/Engine.pm:196 +#: lib/App/Sqitch/Engine.pm:197 msgid "Nothing to deploy (empty plan)" msgstr "Nichts anzuwenden (leerer Plan)" -#: lib/App/Sqitch/Engine.pm:200 lib/App/Sqitch/Engine.pm:294 +#: lib/App/Sqitch/Engine.pm:201 lib/App/Sqitch/Engine.pm:297 #: lib/App/Sqitch/Plan.pm:738 lib/App/Sqitch/Plan/ChangeList.pm:121 #, perl-brace-format msgid "Unknown change: \"{change}\"" msgstr "Unbekannte ร„nderung: \"{change}\"" -#: lib/App/Sqitch/Engine.pm:207 +#: lib/App/Sqitch/Engine.pm:208 #, perl-brace-format msgid "Nothing to deploy (already at \"{change}\")" msgstr "Nichts anzuwenden (aktueller Stand ist bereits \"{change}\")" -#: lib/App/Sqitch/Engine.pm:225 +#: lib/App/Sqitch/Engine.pm:226 #, perl-brace-format msgid "Adding registry tables to {destination}" msgstr "Erstelle Registry-Tabellen in {destination}" -#: lib/App/Sqitch/Engine.pm:234 +#: lib/App/Sqitch/Engine.pm:235 msgid "Cannot deploy to an earlier change; use \"revert\" instead" msgstr "" "Eine vorige ร„nderung kann nicht angewandt werden. Verwende stattdessen " "\"revert\"" -#: lib/App/Sqitch/Engine.pm:242 +#: lib/App/Sqitch/Engine.pm:243 #, perl-brace-format msgid "Deploying changes through {change} to {destination}" msgstr "Wende ร„nderungen von {change} nach {destination} an" -#: lib/App/Sqitch/Engine.pm:246 +#: lib/App/Sqitch/Engine.pm:247 #, perl-brace-format msgid "Deploying changes to {destination}" msgstr "Wende ร„nderungen nach {destination} an" -#: lib/App/Sqitch/Engine.pm:259 +#: lib/App/Sqitch/Engine.pm:260 #, perl-brace-format msgid "Unknown deployment mode: \"{mode}\"" msgstr "Unbekannter Anwendungsmodus: \"{mode}\"" -#: lib/App/Sqitch/Engine.pm:288 +#: lib/App/Sqitch/Engine.pm:291 #, perl-brace-format msgid "Change not deployed: \"{change}\"" msgstr "ร„nderung nicht angewandt: \"{change}\"" -#: lib/App/Sqitch/Engine.pm:303 +#: lib/App/Sqitch/Engine.pm:306 #, perl-brace-format msgid "No changes deployed since: \"{change}\"" msgstr "Keine ร„nderungen seit \"{change}\" angewandt" -#: lib/App/Sqitch/Engine.pm:311 +#: lib/App/Sqitch/Engine.pm:314 #, perl-brace-format msgid "Reverting changes to {change} from {destination}" msgstr "Kehre ร„nderungen an {destination} um nach {change}" -#: lib/App/Sqitch/Engine.pm:318 lib/App/Sqitch/Engine.pm:341 +#: lib/App/Sqitch/Engine.pm:321 lib/App/Sqitch/Engine.pm:344 msgid "Nothing reverted" msgstr "Nichts rรผckgรคngig gemacht" -#: lib/App/Sqitch/Engine.pm:321 +#: lib/App/Sqitch/Engine.pm:324 #, perl-brace-format msgid "Revert changes to {change} from {destination}?" msgstr "ร„nderungen von {change} nach {destination} wieder rรผckgรคngig machen?" -#: lib/App/Sqitch/Engine.pm:329 +#: lib/App/Sqitch/Engine.pm:332 msgid "Nothing to revert (nothing deployed)" msgstr "Nichts rรผckgรคngig zu machen (keine ร„nderungen angewandt)" -#: lib/App/Sqitch/Engine.pm:335 +#: lib/App/Sqitch/Engine.pm:338 #, perl-brace-format msgid "Reverting all changes from {destination}" msgstr "Kehre alle ร„nderungen an {destination} um" -#: lib/App/Sqitch/Engine.pm:344 +#: lib/App/Sqitch/Engine.pm:347 #, perl-brace-format msgid "Revert all changes from {destination}?" msgstr "Alle ร„nderungen an {destination} rรผckgรคngig machen?" -#: lib/App/Sqitch/Engine.pm:375 +#: lib/App/Sqitch/Engine.pm:378 #, perl-brace-format msgid "Verifying {destination}" msgstr "Verifiziere {destination}" -#: lib/App/Sqitch/Engine.pm:382 +#: lib/App/Sqitch/Engine.pm:385 msgid "Nothing to verify (no planned or deployed changes)" msgstr "Nichts zu verifizieren (keine geplanten oder angewandten ร„nderungen)" -#: lib/App/Sqitch/Engine.pm:389 +#: lib/App/Sqitch/Engine.pm:392 msgid "There are deployed changes, but none planned!" msgstr "Angewandte ร„nderungen gefunden die ungeplant sind!" -#: lib/App/Sqitch/Engine.pm:402 +#: lib/App/Sqitch/Engine.pm:405 msgid "Verify Summary Report" msgstr "Verifizierungsreport" -#: lib/App/Sqitch/Engine.pm:405 +#: lib/App/Sqitch/Engine.pm:408 #, perl-brace-format msgid "Changes: {number}" msgstr "ร„nderungen: {number}" -#: lib/App/Sqitch/Engine.pm:406 +#: lib/App/Sqitch/Engine.pm:409 #, perl-brace-format msgid "Errors: {number}" msgstr "Fehler: {number}" -#: lib/App/Sqitch/Engine.pm:407 +#: lib/App/Sqitch/Engine.pm:410 msgid "Verify failed" msgstr "Verifizierung fehlgeschlagen" -#: lib/App/Sqitch/Engine.pm:412 +#: lib/App/Sqitch/Engine.pm:415 msgid "Verify successful" msgstr "Erfolgreich verifiziert" -#: lib/App/Sqitch/Engine.pm:441 +#: lib/App/Sqitch/Engine.pm:444 #, perl-brace-format msgid "Change \"{change}\" has not been deployed" msgstr "ร„nderung \"{change}\" wurde noch nicht angewendet" -#: lib/App/Sqitch/Engine.pm:444 +#: lib/App/Sqitch/Engine.pm:447 #, perl-brace-format msgid "Cannot find \"{change}\" in the database or the plan" msgstr "Kann \"{change}\" weder in der Datenbank noch im Plan finden" -#: lib/App/Sqitch/Engine.pm:451 +#: lib/App/Sqitch/Engine.pm:454 #, perl-brace-format msgid "Change \"{change}\" is deployed, but not planned" msgstr "ร„nderung \"{change}\" wurde angewendet, allerdings nicht geplant" -#: lib/App/Sqitch/Engine.pm:495 +#: lib/App/Sqitch/Engine.pm:498 msgid "Out of order" msgstr "Ungรผltige Reihenfolge" -#: lib/App/Sqitch/Engine.pm:501 +#: lib/App/Sqitch/Engine.pm:504 msgid "Not present in the plan" msgstr "Im Plan nicht vorhanden" -#: lib/App/Sqitch/Engine.pm:512 lib/App/Sqitch/Engine.pm:524 -#: lib/App/Sqitch/Engine.pm:993 lib/App/Sqitch/Engine.pm:1023 +#: lib/App/Sqitch/Engine.pm:515 lib/App/Sqitch/Engine.pm:527 +#: lib/App/Sqitch/Engine.pm:996 lib/App/Sqitch/Engine.pm:1026 msgid "not ok" msgstr "nicht OK" -#: lib/App/Sqitch/Engine.pm:512 lib/App/Sqitch/Engine.pm:971 -#: lib/App/Sqitch/Engine.pm:1013 +#: lib/App/Sqitch/Engine.pm:515 lib/App/Sqitch/Engine.pm:974 +#: lib/App/Sqitch/Engine.pm:1016 msgid "ok" msgstr "OK" -#: lib/App/Sqitch/Engine.pm:526 +#: lib/App/Sqitch/Engine.pm:529 msgid "Not deployed" msgstr "Nicht angewendet" -#: lib/App/Sqitch/Engine.pm:558 +#: lib/App/Sqitch/Engine.pm:561 #, perl-brace-format msgid "Verify script \"{script}\" failed." msgstr "Verifizierungsskript {script} fehlgeschlagen." -#: lib/App/Sqitch/Engine.pm:567 +#: lib/App/Sqitch/Engine.pm:570 #, perl-brace-format msgid "Verify script {file} does not exist" msgstr "Verifizierungsskript {file} existiert nicht" -#: lib/App/Sqitch/Engine.pm:609 +#: lib/App/Sqitch/Engine.pm:612 #, perl-brace-format msgid "Conflicts with previously deployed change: {changes}" msgid_plural "Conflicts with previously deployed changes: {changes}" msgstr[0] "Konflikte mit zuvor angewendeter ร„nderung: {changes}" msgstr[1] "Konflikte mit zuvor angewendeten ร„nderungen: {changes}" -#: lib/App/Sqitch/Engine.pm:616 +#: lib/App/Sqitch/Engine.pm:619 #, perl-brace-format msgid "Missing required change: {changes}" msgid_plural "Missing required changes: {changes}" msgstr[0] "Fehlende benรถtigte ร„nderung: {changes}" msgstr[1] "Fehlende benรถtigte ร„nderungen: {changes}" -#: lib/App/Sqitch/Engine.pm:628 +#: lib/App/Sqitch/Engine.pm:631 #, perl-brace-format msgid "Change \"{changes}\" has already been deployed" msgid_plural "Changes have already been deployed: {changes}" msgstr[0] "ร„nderung \"{changes}\" wurde bereits angewendet" msgstr[1] "ร„nderungen wurden bereits angewendet: {changes}" -#: lib/App/Sqitch/Engine.pm:651 +#: lib/App/Sqitch/Engine.pm:654 #, perl-brace-format msgid "Change \"{change}\" required by currently deployed change: {changes}" msgid_plural "" @@ -921,66 +886,66 @@ msgstr[1] "" "ร„nderung \"{change}\" wird erfordert von gerade angewendeten ร„nderungen: " "{changes}" -#: lib/App/Sqitch/Engine.pm:674 +#: lib/App/Sqitch/Engine.pm:677 #, perl-brace-format msgid "Invalid dependency: {dependency}" msgstr "Ungรผltige Abhรคngigkeit: {dependency}" -#: lib/App/Sqitch/Engine.pm:810 lib/App/Sqitch/Plan/ChangeList.pm:88 +#: lib/App/Sqitch/Engine.pm:813 lib/App/Sqitch/Plan/ChangeList.pm:88 #, perl-brace-format msgid "" "Change \"{change}\" is ambiguous. Please specify a tag-qualified change:" msgstr "" "ร„nderung \"{change}\" ist mehrdeutig. Bitte gib ein Tag fรผr die ร„nderung an:" -#: lib/App/Sqitch/Engine.pm:825 +#: lib/App/Sqitch/Engine.pm:828 msgid "Change Lookup Failed" msgstr "Suche nach ร„nderung gescheitert" -#: lib/App/Sqitch/Engine.pm:846 +#: lib/App/Sqitch/Engine.pm:849 #, perl-brace-format msgid "Reverting to {change}" msgstr "Mache ร„nderungen rรผckgรคnging bis nach {change}" -#: lib/App/Sqitch/Engine.pm:847 +#: lib/App/Sqitch/Engine.pm:850 msgid "Reverting all changes" msgstr "Kehre sรคmtliche ร„nderungen um" -#: lib/App/Sqitch/Engine.pm:855 +#: lib/App/Sqitch/Engine.pm:858 msgid "The schema will need to be manually repaired" msgstr "Das Schema muss manuell repariert werden" -#: lib/App/Sqitch/Engine.pm:859 lib/App/Sqitch/Engine.pm:987 +#: lib/App/Sqitch/Engine.pm:862 lib/App/Sqitch/Engine.pm:990 msgid "Deploy failed" msgstr "Anwendung der ร„nderung(en) fehlgeschlagen" -#: lib/App/Sqitch/Engine.pm:916 +#: lib/App/Sqitch/Engine.pm:919 #, perl-brace-format msgid "Cannot find change {id} ({change}) in {file}" msgstr "Kann ร„nderung {id} ({change}) in {file} nicht finden" -#: lib/App/Sqitch/Engine.pm:1038 +#: lib/App/Sqitch/Engine.pm:1041 #, perl-brace-format msgid "" "Blocked by another instance of Sqitch working on {dest}; waiting {secs} " "seconds..." msgstr "" -#: lib/App/Sqitch/Engine.pm:1048 +#: lib/App/Sqitch/Engine.pm:1051 #, perl-brace-format msgid "" "Timed out waiting {secs} seconds for another instance of Sqitch to finish " "work on {dest}" msgstr "" -#: lib/App/Sqitch/Engine.pm:1108 +#: lib/App/Sqitch/Engine.pm:1110 #, perl-brace-format msgid "No registry found in {destination}. Have you ever deployed?" msgstr "" "Registry nicht gefunden in {destination}. Wurde Sqitch jemals darauf " "ausgefรผhrt?" -#: lib/App/Sqitch/Engine.pm:1113 +#: lib/App/Sqitch/Engine.pm:1115 #, perl-brace-format msgid "" "Registry version is {old} but {new} is the latest known. Please upgrade " @@ -989,7 +954,7 @@ msgstr "" "Aktuelle Registry-Version ist {old} allerdings ist {new} die neueste " "bekannte Version. Bitte aktualisiere Sqitch" -#: lib/App/Sqitch/Engine.pm:1119 +#: lib/App/Sqitch/Engine.pm:1121 #, perl-brace-format msgid "" "Registry is at version {old} but latest is {new}. Please run the \"upgrade\" " @@ -998,7 +963,7 @@ msgstr "" "Registry ist derzeit auf Version {old} aber neueste Version ist {new}. Bitte " "fรผhre den \"upgrade\"-Befehl aus" -#: lib/App/Sqitch/Engine.pm:1134 +#: lib/App/Sqitch/Engine.pm:1136 #, perl-brace-format msgid "" "Registry version is {old} but {new} is the latest known. Please upgrade " @@ -1007,65 +972,65 @@ msgstr "" "Aktuelle Registry-Version ist {old} allerdings ist {new} die neueste " "bekannte Version. Bitte aktualisiere Sqitch" -#: lib/App/Sqitch/Engine.pm:1149 +#: lib/App/Sqitch/Engine.pm:1151 #, perl-brace-format msgid "Cannot upgrade to {version}: Cannot find upgrade script \"{file}\"" msgstr "" "Kann nicht nach {version} aktualisieren: Upgrade-Skript \"{file}\" nicht " "gefunden" -#: lib/App/Sqitch/Engine.pm:1156 +#: lib/App/Sqitch/Engine.pm:1158 #, perl-brace-format msgid "Upgrading the Sqitch registry from {old} to {new}" msgstr "Aktualisiere die Sqitch-Registry von {old} nach {new}" -#: lib/App/Sqitch/Engine.pm:1163 +#: lib/App/Sqitch/Engine.pm:1165 #, perl-brace-format msgid "From {old} to {new}" msgstr "Von {old} nach {new}" -#: lib/App/Sqitch/Engine.pm:1208 +#: lib/App/Sqitch/Engine.pm:1210 #, fuzzy, perl-brace-format msgid "Checking {destination}" msgstr "Verifiziere {destination}" -#: lib/App/Sqitch/Engine.pm:1215 +#: lib/App/Sqitch/Engine.pm:1217 #, fuzzy msgid "Nothing to check (no planned or deployed changes)" msgstr "Nichts zu verifizieren (keine geplanten oder angewandten ร„nderungen)" -#: lib/App/Sqitch/Engine.pm:1228 +#: lib/App/Sqitch/Engine.pm:1230 #, fuzzy, perl-brace-format msgid "Script signatures diverge at change {change}" msgstr "Fehlende benรถtigte ร„nderung: {changes}" -#: lib/App/Sqitch/Engine.pm:1236 +#: lib/App/Sqitch/Engine.pm:1238 #, perl-brace-format msgid "Failed one check" msgid_plural "Failed {count} checks" msgstr[0] "" msgstr[1] "" -#: lib/App/Sqitch/Engine.pm:1244 +#: lib/App/Sqitch/Engine.pm:1246 #, fuzzy msgid "Check successful" msgstr "Erfolgreich verifiziert" -#: lib/App/Sqitch/Engine/exasol.pm:324 lib/App/Sqitch/Engine/oracle.pm:455 +#: lib/App/Sqitch/Engine/exasol.pm:324 lib/App/Sqitch/Engine/oracle.pm:458 msgid "Sqitch already initialized" msgstr "Sqitch ist bereits initialisiert" -#: lib/App/Sqitch/Engine/exasol.pm:392 lib/App/Sqitch/Engine/oracle.pm:585 +#: lib/App/Sqitch/Engine/exasol.pm:392 lib/App/Sqitch/Engine/oracle.pm:588 #, perl-brace-format msgid "Cannot remove {file}: {error}" msgstr "Kann Datei {file} nicht entfernen: {error}" -#: lib/App/Sqitch/Engine/exasol.pm:401 lib/App/Sqitch/Engine/oracle.pm:594 +#: lib/App/Sqitch/Engine/exasol.pm:401 lib/App/Sqitch/Engine/oracle.pm:597 #, perl-brace-format msgid "Cannot copy {file} to {alias}: {error}" msgstr "Kann Datei {file} nicht nach {alias} kopieren: {error}" -#: lib/App/Sqitch/Engine/exasol.pm:410 lib/App/Sqitch/Engine/oracle.pm:603 +#: lib/App/Sqitch/Engine/exasol.pm:410 lib/App/Sqitch/Engine/oracle.pm:606 #, perl-brace-format msgid "Cannot symlink {file} to {alias}: {error}" msgstr "" @@ -1076,34 +1041,34 @@ msgstr "" msgid "{command} unexpectedly failed; exit value = {exitval}" msgstr "{command} unerwartet gescheitert. Rรผckgabewert = {exitval}" -#: lib/App/Sqitch/Engine/firebird.pm:207 lib/App/Sqitch/Engine/mysql.pm:274 +#: lib/App/Sqitch/Engine/firebird.pm:208 lib/App/Sqitch/Engine/mysql.pm:281 #: lib/App/Sqitch/Engine/sqlite.pm:158 #, perl-brace-format msgid "Sqitch database {database} already initialized" msgstr "Sqitch-Datenbank {database} ist bereits initialisiert" -#: lib/App/Sqitch/Engine/firebird.pm:226 +#: lib/App/Sqitch/Engine/firebird.pm:227 #, perl-brace-format msgid "Cannot create database {database}: {error}" msgstr "Kann Datenbank {database} nicht erstellen: {error}" -#: lib/App/Sqitch/Engine/firebird.pm:240 lib/App/Sqitch/Engine/sqlite.pm:127 +#: lib/App/Sqitch/Engine/firebird.pm:241 lib/App/Sqitch/Engine/sqlite.pm:127 #, perl-brace-format msgid "Database name missing in URI {uri}" msgstr "Name der Datenbank fehlt in URI {uri}" -#: lib/App/Sqitch/Engine/firebird.pm:881 lib/App/Sqitch/Engine/firebird.pm:898 -#: lib/App/Sqitch/Engine/firebird.pm:909 +#: lib/App/Sqitch/Engine/firebird.pm:889 lib/App/Sqitch/Engine/firebird.pm:907 +#: lib/App/Sqitch/Engine/firebird.pm:918 #, perl-brace-format msgid "Cannot dup STDERR: {error}" msgstr "Kann STDERR nicht รถffnen: {error}" -#: lib/App/Sqitch/Engine/firebird.pm:885 +#: lib/App/Sqitch/Engine/firebird.pm:893 #, perl-brace-format msgid "Cannot reirect STDERR: {error}" msgstr "Kann nicht nach STDERR umleiten: {error}" -#: lib/App/Sqitch/Engine/firebird.pm:912 +#: lib/App/Sqitch/Engine/firebird.pm:921 msgid "" "Unable to locate Firebird ISQL; set \"engine.firebird.client\" via sqitch " "config" @@ -1124,18 +1089,18 @@ msgstr "" msgid "Database name missing in URI \"{uri}\"" msgstr "Name der Datenbank fehlt in URI \"{uri}\"" -#: lib/App/Sqitch/Engine/pg.pm:189 lib/App/Sqitch/Engine/snowflake.pm:300 +#: lib/App/Sqitch/Engine/pg.pm:212 lib/App/Sqitch/Engine/snowflake.pm:300 #: lib/App/Sqitch/Engine/vertica.pm:142 #, perl-brace-format msgid "Sqitch schema \"{schema}\" already exists" msgstr "Sqitch-Schema \"{schema}\" existiert bereits" -#: lib/App/Sqitch/Engine/pg.pm:412 +#: lib/App/Sqitch/Engine/pg.pm:452 #, fuzzy msgid "Sqitch registry not initialized" msgstr "Sqitch ist bereits initialisiert" -#: lib/App/Sqitch/Engine/pg.pm:413 +#: lib/App/Sqitch/Engine/pg.pm:453 msgid "" "Because the \"changes\" table does not exist, Sqitch will now initialize the " "database to create its registry tables." @@ -1618,3 +1583,32 @@ msgstr "Benutzername darf nicht \"<\" beinhalten oder mit \"[\" beginnen" #: lib/App/Sqitch/Types.pm:63 msgid "User email may not contain \">\"" msgstr "Benutzer E-Mail-Adresse darf nicht \">\" beinhalten" + +#, perl-brace-format +#~ msgid "Loading {file}" +#~ msgstr "Lade {file}" + +#, perl-brace-format +#~ msgid "" +#~ "Deprecated {section} found in {file}; to remove it, run\n" +#~ " {sqitch} config --file {file} --remove-section {section}" +#~ msgstr "" +#~ "Veraltete Sektion {section} gefunden in {file}. Zum Entfernen fรผhre den " +#~ "folgenden Befehl aus:\n" +#~ " {sqitch} config --file {file} --remove-section {section}" + +#~ msgid " - No engines to update" +#~ msgstr " - Keine Engines zum Aktualisieren verfรผgbar" + +#, perl-brace-format +#~ msgid "Cannot update {file}. Please make it writable" +#~ msgstr "Kann {file} nicht aktualisieren. Bitte mache die Datei schreibbar" + +#, perl-brace-format +#~ msgid "" +#~ "Migrated {old} to {new}; To remove {old}, run\n" +#~ " {sqitch} config --file {file} --remove-section {old}" +#~ msgstr "" +#~ "{old} nach {new} migriert. Um {old} zu entfernen, fรผhre bitte den " +#~ "folgenden Befehl aus:\n" +#~ " {sqitch} config --file {file} --remove-section {old}" diff --git a/po/fr_FR.po b/po/fr_FR.po index 2b549cdcd..e263e1801 100644 --- a/po/fr_FR.po +++ b/po/fr_FR.po @@ -1,5 +1,5 @@ # Sqitch French Localization Messages -# Copyright (c) 2012-2021 iovation Inc., David E. Wheeler +# Copyright (c) 2012-2022 iovation Inc., David E. Wheeler # This file is distributed under the same license as the App-Sqitch package. # Arnaud Assad , 2012. # @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: Sqitch 0.932\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-11-14 19:00-0500\n" +"POT-Creation-Date: 2022-08-12 16:04-0400\n" "PO-Revision-Date: 2012-10-12 11:28-0700\n" "Last-Translator: Arnaud Assad \n" "Language-Team: French \n" @@ -31,118 +31,125 @@ msgstr "" "Impossible de dรฉduire votre adressse email; exรฉcuter sqitch config --user " "user.email vous@hote.com" -#: lib/App/Sqitch.pm:282 +#: lib/App/Sqitch.pm:280 #, fuzzy, perl-brace-format msgid "Cannot change to directory {directory}: {error}" msgstr "Impossible de copier {src} vers {dest} : {error}" -#: lib/App/Sqitch.pm:316 lib/App/Sqitch/Command.pm:112 +#: lib/App/Sqitch.pm:314 lib/App/Sqitch/Command.pm:113 #, perl-brace-format msgid "\"{command}\" is not a valid command" msgstr "\"{command}\" n'est pas une commande valide" -#: lib/App/Sqitch.pm:410 +#: lib/App/Sqitch.pm:408 msgid "" "Sqitch seems to be unattended and there is no default value for this question" msgstr "" -#: lib/App/Sqitch.pm:429 +#: lib/App/Sqitch.pm:427 msgctxt "Confirm prompt answer yes" msgid "Yes" msgstr "" -#: lib/App/Sqitch.pm:430 +#: lib/App/Sqitch.pm:428 msgctxt "Confirm prompt answer no" msgid "No" msgstr "" -#: lib/App/Sqitch.pm:439 +#: lib/App/Sqitch.pm:437 msgid "Please answer \"y\" or \"n\"." msgstr "" -#: lib/App/Sqitch.pm:442 +#: lib/App/Sqitch.pm:440 msgid "No valid answer after 3 attempts; aborting" msgstr "" -#: lib/App/Sqitch.pm:463 lib/App/Sqitch.pm:470 +#: lib/App/Sqitch.pm:461 lib/App/Sqitch.pm:468 #, perl-brace-format msgid "Cannot exec {command}: {error}" msgstr "Impossible d'exรฉcuter {command} : {error}" -#: lib/App/Sqitch.pm:486 +#: lib/App/Sqitch.pm:484 #, perl-brace-format msgid "Error closing pipe to {command}: {error}" msgstr "Erreur en fermant le tube vers {command} : {error}" -#: lib/App/Sqitch.pm:490 lib/App/Sqitch/Engine/oracle.pm:758 +#: lib/App/Sqitch.pm:488 lib/App/Sqitch/Engine/oracle.pm:761 #, perl-brace-format msgid "{command} unexpectedly returned exit value {exitval}" msgstr "{command} a retournรฉ de maniรจre inattendue la valeur {exitval}" -#: lib/App/Sqitch/Command.pm:282 +#: lib/App/Sqitch/Command.pm:283 #, fuzzy, perl-brace-format msgid "Unknown argument \"{arg}\"" msgid_plural "Unknown arguments: {arg}" msgstr[0] "ร‰tiquette inconnue \"{tag}\"" msgstr[1] "ร‰tiquette inconnue \"{tag}\"" -#: lib/App/Sqitch/Command.pm:294 +#: lib/App/Sqitch/Command.pm:295 msgid "Cannot specify both --all and engine, target, or plan arugments" msgstr "" -#: lib/App/Sqitch/Command/add.pm:103 +#: lib/App/Sqitch/Command.pm:315 lib/App/Sqitch/Command/add.pm:410 +#: lib/App/Sqitch/Command/init.pm:203 +#: lib/App/Sqitch/Role/TargetConfigCommand.pm:250 +#: lib/App/Sqitch/Role/TargetConfigCommand.pm:337 +#, perl-brace-format +msgid "Created {file}" +msgstr "{file} crรฉe" + +#: lib/App/Sqitch/Command.pm:322 lib/App/Sqitch/Role/TargetConfigCommand.pm:256 +#, perl-brace-format +msgid "Error creating {path}: {error}" +msgstr "Erreur ร  la crรฉation de {path} : {error}" + +#: lib/App/Sqitch/Command/add.pm:102 #, fuzzy, perl-brace-format msgid "Template {template} does not exist" msgstr "Le fichier de plan {file} n'existe pas" -#: lib/App/Sqitch/Command/add.pm:108 +#: lib/App/Sqitch/Command/add.pm:107 #, fuzzy, perl-brace-format msgid "Template {template} is not a file" msgstr "\"{command}\" n'est pas une commande valide" -#: lib/App/Sqitch/Command/add.pm:146 +#: lib/App/Sqitch/Command/add.pm:145 #, perl-brace-format msgid "Cannot find {script} template" msgstr "Impossible de trouver le patron {script}" -#: lib/App/Sqitch/Command/add.pm:225 +#: lib/App/Sqitch/Command/add.pm:224 #, fuzzy, perl-brace-format msgid "Directory \"{dir}\" does not exist" msgstr "Le fichier de plan {file} n'existe pas" -#: lib/App/Sqitch/Command/add.pm:230 +#: lib/App/Sqitch/Command/add.pm:229 #, fuzzy, perl-brace-format msgid "\"{dir}\" is not a directory" msgstr "\"{command}\" n'est pas une commande valide" -#: lib/App/Sqitch/Command/add.pm:283 +#: lib/App/Sqitch/Command/add.pm:282 #, perl-brace-format msgid "" "Name \"{name}\" identifies a target; use \"--change {name}\" to use it for " "the change name" msgstr "" -#: lib/App/Sqitch/Command/add.pm:331 +#: lib/App/Sqitch/Command/add.pm:330 msgid "add" msgstr "ajouter" -#: lib/App/Sqitch/Command/add.pm:349 +#: lib/App/Sqitch/Command/add.pm:348 #, perl-brace-format msgid "Added \"{change}\" to {file}" msgstr "Ajoutรฉ \"{change}\" ร  {file}" -#: lib/App/Sqitch/Command/add.pm:368 +#: lib/App/Sqitch/Command/add.pm:367 #, perl-brace-format msgid "Skipped {file}: already exists" msgstr "Ignorรฉ {file}: existe dรฉjร " -#: lib/App/Sqitch/Command/add.pm:379 lib/App/Sqitch/Command/bundle.pm:141 -#: lib/App/Sqitch/Role/TargetConfigCommand.pm:256 -#, perl-brace-format -msgid "Error creating {path}: {error}" -msgstr "Erreur ร  la crรฉation de {path} : {error}" - -#: lib/App/Sqitch/Command/add.pm:396 lib/App/Sqitch/Command/add.pm:426 +#: lib/App/Sqitch/Command/add.pm:386 lib/App/Sqitch/Command/add.pm:416 #: lib/App/Sqitch/Plan.pm:134 lib/App/Sqitch/Plan.pm:591 #: lib/App/Sqitch/Plan.pm:972 lib/App/Sqitch/Plan/Line.pm:107 #: lib/App/Sqitch/Role/TargetConfigCommand.pm:321 @@ -150,25 +157,17 @@ msgstr "Erreur ร  la crรฉation de {path} : {error}" msgid "Cannot open {file}: {error}" msgstr "Impossible d'ouvrir {file} : {error}" -#: lib/App/Sqitch/Command/add.pm:404 +#: lib/App/Sqitch/Command/add.pm:394 #, fuzzy, perl-brace-format msgid "Error executing {template}: {error}" msgstr "Erreur ร  la crรฉation de {path} : {error}" -#: lib/App/Sqitch/Command/add.pm:416 +#: lib/App/Sqitch/Command/add.pm:406 #: lib/App/Sqitch/Role/TargetConfigCommand.pm:332 #, perl-brace-format msgid "Error closing {file}: {error}" msgstr "Erreur ร  la fermeture de {file} : {error}" -#: lib/App/Sqitch/Command/add.pm:420 lib/App/Sqitch/Command/bundle.pm:134 -#: lib/App/Sqitch/Command/init.pm:203 -#: lib/App/Sqitch/Role/TargetConfigCommand.pm:250 -#: lib/App/Sqitch/Role/TargetConfigCommand.pm:337 -#, perl-brace-format -msgid "Created {file}" -msgstr "{file} crรฉe" - #: lib/App/Sqitch/Command/bundle.pm:99 msgid "" "Use of --to or --from to bundle multiple targets is not recommended.\n" @@ -184,41 +183,41 @@ msgstr "" msgid "Bundling into {dir}" msgstr "Groupement dans {dir}" -#: lib/App/Sqitch/Command/bundle.pm:152 +#: lib/App/Sqitch/Command/bundle.pm:136 #, perl-brace-format msgid "Cannot copy {file}: does not exist" msgstr "Impossible de copier {file} : il n'existe pas" -#: lib/App/Sqitch/Command/bundle.pm:165 +#: lib/App/Sqitch/Command/bundle.pm:149 #, perl-brace-format msgid "Copying {source} -> {dest}" msgstr "Copie {source} -> {dest}" -#: lib/App/Sqitch/Command/bundle.pm:172 +#: lib/App/Sqitch/Command/bundle.pm:156 #, perl-brace-format msgid "Cannot copy \"{source}\" to \"{dest}\": {error}" msgstr "Impossible de copier \"{source}\" vers \"{dest}\" : {error}" -#: lib/App/Sqitch/Command/bundle.pm:182 +#: lib/App/Sqitch/Command/bundle.pm:166 msgid "Writing config" msgstr "ร‰criture de la configuration" -#: lib/App/Sqitch/Command/bundle.pm:193 +#: lib/App/Sqitch/Command/bundle.pm:177 msgid "Writing plan" msgstr "ร‰criture du plan" -#: lib/App/Sqitch/Command/bundle.pm:202 +#: lib/App/Sqitch/Command/bundle.pm:186 #, perl-brace-format msgid "Writing plan from {from} to {to}" msgstr "ร‰criture du plan de {from} ร  {to}" -#: lib/App/Sqitch/Command/bundle.pm:222 lib/App/Sqitch/Command/bundle.pm:229 +#: lib/App/Sqitch/Command/bundle.pm:206 lib/App/Sqitch/Command/bundle.pm:213 #: lib/App/Sqitch/Plan.pm:944 lib/App/Sqitch/Plan.pm:953 #, perl-brace-format msgid "Cannot find change {change}" msgstr "Impossible de trouver le changement {change}" -#: lib/App/Sqitch/Command/bundle.pm:233 +#: lib/App/Sqitch/Command/bundle.pm:217 msgid "Writing scripts" msgstr "ร‰criture des scripts" @@ -374,34 +373,6 @@ msgstr "" msgid "No Variables" msgstr "" -#: lib/App/Sqitch/Command/engine.pm:253 -#, fuzzy, perl-brace-format -msgid "Loading {file}" -msgstr "{file} crรฉe" - -#: lib/App/Sqitch/Command/engine.pm:266 -#, perl-brace-format -msgid "" -"Deprecated {section} found in {file}; to remove it, run\n" -" {sqitch} config --file {file} --remove-section {section}" -msgstr "" - -#: lib/App/Sqitch/Command/engine.pm:278 -msgid " - No engines to update" -msgstr "" - -#: lib/App/Sqitch/Command/engine.pm:285 -#, perl-brace-format -msgid "Cannot update {file}. Please make it writable" -msgstr "" - -#: lib/App/Sqitch/Command/engine.pm:341 -#, perl-brace-format -msgid "" -"Migrated {old} to {new}; To remove {old}, run\n" -" {sqitch} config --file {file} --remove-section {old}" -msgstr "" - #: lib/App/Sqitch/Command/help.pm:46 #, perl-brace-format msgid "No manual entry for {command}" @@ -443,7 +414,7 @@ msgstr "Sur la Base de Donnรฉes {db}" msgid "Unknown plan format \"{format}\"" msgstr "Format de journalisation \"{format}\" inconnu" -#: lib/App/Sqitch/Command/plan.pm:199 lib/App/Sqitch/Command/upgrade.pm:40 +#: lib/App/Sqitch/Command/plan.pm:199 lib/App/Sqitch/Command/upgrade.pm:39 #, perl-brace-format msgid "Too many targets specified; using {target}" msgstr "" @@ -547,8 +518,8 @@ msgstr "" msgid ", " msgstr ", " -#: lib/App/Sqitch/Command/status.pm:138 lib/App/Sqitch/Engine.pm:381 -#: lib/App/Sqitch/Engine.pm:1214 +#: lib/App/Sqitch/Command/status.pm:138 lib/App/Sqitch/Engine.pm:384 +#: lib/App/Sqitch/Engine.pm:1216 msgid "No changes deployed" msgstr "Pas de changements dรฉployรฉs" @@ -615,11 +586,11 @@ msgstr "Impossible de trouver ce changement dans {file}" msgid "Make sure you are connected to the proper database for this project." msgstr "Assurez-vous d'รชtre connectรฉ ร  la bonne base de donnรฉes pour ce projet" -#: lib/App/Sqitch/Command/status.pm:308 lib/App/Sqitch/Engine.pm:216 +#: lib/App/Sqitch/Command/status.pm:308 lib/App/Sqitch/Engine.pm:217 msgid "Nothing to deploy (up-to-date)" msgstr "Rien ร  dรฉployer (ร  jour)" -#: lib/App/Sqitch/Command/status.pm:311 lib/App/Sqitch/Engine.pm:536 +#: lib/App/Sqitch/Command/status.pm:311 lib/App/Sqitch/Engine.pm:539 msgid "Undeployed change:" msgid_plural "Undeployed changes:" msgstr[0] "Changement non dรฉployรฉ :" @@ -660,12 +631,12 @@ msgstr "" msgid "URI" msgstr "" -#: lib/App/Sqitch/Command/upgrade.pm:48 +#: lib/App/Sqitch/Command/upgrade.pm:47 #, fuzzy, perl-brace-format msgid "Upgrading registry {registry} to version {version}" msgstr "Ajout des tables de metadonnรฉes ร  {destination}" -#: lib/App/Sqitch/Command/upgrade.pm:55 +#: lib/App/Sqitch/Command/upgrade.pm:54 #, perl-brace-format msgid "Registry {registry} is up-to-date at version {version}" msgstr "" @@ -695,196 +666,196 @@ msgstr "Pas de moteur spรฉcifiรฉ; utiliser --engine ou dรฉfinir core.engine" msgid "{driver} required to manage {engine}" msgstr "Module DBD::Pg requis pour gรฉrer PostgreSQL" -#: lib/App/Sqitch/Engine.pm:196 +#: lib/App/Sqitch/Engine.pm:197 msgid "Nothing to deploy (empty plan)" msgstr "Rien ร  dรฉployer (plan vide)" -#: lib/App/Sqitch/Engine.pm:200 lib/App/Sqitch/Engine.pm:294 +#: lib/App/Sqitch/Engine.pm:201 lib/App/Sqitch/Engine.pm:297 #: lib/App/Sqitch/Plan.pm:738 lib/App/Sqitch/Plan/ChangeList.pm:121 #, perl-brace-format msgid "Unknown change: \"{change}\"" msgstr "Changement inconnu \"{change}\" " -#: lib/App/Sqitch/Engine.pm:207 +#: lib/App/Sqitch/Engine.pm:208 #, fuzzy, perl-brace-format msgid "Nothing to deploy (already at \"{change}\")" msgstr "Rien ร  dรฉployer (dรฉjร  sur \"{target}\"" -#: lib/App/Sqitch/Engine.pm:225 +#: lib/App/Sqitch/Engine.pm:226 #, fuzzy, perl-brace-format msgid "Adding registry tables to {destination}" msgstr "Ajout des tables de metadonnรฉes ร  {destination}" -#: lib/App/Sqitch/Engine.pm:234 +#: lib/App/Sqitch/Engine.pm:235 #, fuzzy msgid "Cannot deploy to an earlier change; use \"revert\" instead" msgstr "" "Impossible de dรฉployer vers une cible antรฉrieure; utiliser \"revert\" plutรดt" -#: lib/App/Sqitch/Engine.pm:242 +#: lib/App/Sqitch/Engine.pm:243 #, fuzzy, perl-brace-format msgid "Deploying changes through {change} to {destination}" msgstr "Dรฉploiement des changements de {target} ร  {destination}" -#: lib/App/Sqitch/Engine.pm:246 +#: lib/App/Sqitch/Engine.pm:247 #, perl-brace-format msgid "Deploying changes to {destination}" msgstr "Dรฉploiement des changements vers {destination}" -#: lib/App/Sqitch/Engine.pm:259 +#: lib/App/Sqitch/Engine.pm:260 #, perl-brace-format msgid "Unknown deployment mode: \"{mode}\"" msgstr "Mode de dรฉploiement inconnu : \"{mode}\"" -#: lib/App/Sqitch/Engine.pm:288 +#: lib/App/Sqitch/Engine.pm:291 #, fuzzy, perl-brace-format msgid "Change not deployed: \"{change}\"" msgstr "Cible non dรฉployรฉe : \"{target}\"" -#: lib/App/Sqitch/Engine.pm:303 +#: lib/App/Sqitch/Engine.pm:306 #, fuzzy, perl-brace-format msgid "No changes deployed since: \"{change}\"" msgstr "Cible non dรฉployรฉe : \"{target}\"" -#: lib/App/Sqitch/Engine.pm:311 +#: lib/App/Sqitch/Engine.pm:314 #, fuzzy, perl-brace-format msgid "Reverting changes to {change} from {destination}" msgstr "Annulation des changements de {destination} ร  {target}" -#: lib/App/Sqitch/Engine.pm:318 lib/App/Sqitch/Engine.pm:341 +#: lib/App/Sqitch/Engine.pm:321 lib/App/Sqitch/Engine.pm:344 #, fuzzy msgid "Nothing reverted" msgstr "Rien ร  annuler (rien de dรฉployรฉ)" -#: lib/App/Sqitch/Engine.pm:321 +#: lib/App/Sqitch/Engine.pm:324 #, fuzzy, perl-brace-format msgid "Revert changes to {change} from {destination}?" msgstr "Annulation des changements de {destination} ร  {target}" -#: lib/App/Sqitch/Engine.pm:329 +#: lib/App/Sqitch/Engine.pm:332 msgid "Nothing to revert (nothing deployed)" msgstr "Rien ร  annuler (rien de dรฉployรฉ)" -#: lib/App/Sqitch/Engine.pm:335 +#: lib/App/Sqitch/Engine.pm:338 #, perl-brace-format msgid "Reverting all changes from {destination}" msgstr "Annulation de tous les changements de {destination}" -#: lib/App/Sqitch/Engine.pm:344 +#: lib/App/Sqitch/Engine.pm:347 #, fuzzy, perl-brace-format msgid "Revert all changes from {destination}?" msgstr "Annulation de tous les changements de {destination}" -#: lib/App/Sqitch/Engine.pm:375 +#: lib/App/Sqitch/Engine.pm:378 #, fuzzy, perl-brace-format msgid "Verifying {destination}" msgstr "Dรฉploiement des changements vers {destination}" -#: lib/App/Sqitch/Engine.pm:382 +#: lib/App/Sqitch/Engine.pm:385 #, fuzzy msgid "Nothing to verify (no planned or deployed changes)" msgstr "Rien ร  annuler (rien de dรฉployรฉ)" -#: lib/App/Sqitch/Engine.pm:389 +#: lib/App/Sqitch/Engine.pm:392 msgid "There are deployed changes, but none planned!" msgstr "" -#: lib/App/Sqitch/Engine.pm:402 +#: lib/App/Sqitch/Engine.pm:405 msgid "Verify Summary Report" msgstr "" -#: lib/App/Sqitch/Engine.pm:405 +#: lib/App/Sqitch/Engine.pm:408 #, fuzzy, perl-brace-format msgid "Changes: {number}" msgstr "Changement : {change_id}" -#: lib/App/Sqitch/Engine.pm:406 +#: lib/App/Sqitch/Engine.pm:409 #, perl-brace-format msgid "Errors: {number}" msgstr "" -#: lib/App/Sqitch/Engine.pm:407 +#: lib/App/Sqitch/Engine.pm:410 #, fuzzy msgid "Verify failed" msgstr "Echec du dรฉploiement" -#: lib/App/Sqitch/Engine.pm:412 +#: lib/App/Sqitch/Engine.pm:415 msgid "Verify successful" msgstr "" -#: lib/App/Sqitch/Engine.pm:441 +#: lib/App/Sqitch/Engine.pm:444 #, perl-brace-format msgid "Change \"{change}\" has not been deployed" msgstr "" -#: lib/App/Sqitch/Engine.pm:444 +#: lib/App/Sqitch/Engine.pm:447 #, fuzzy, perl-brace-format msgid "Cannot find \"{change}\" in the database or the plan" msgstr "Impossible de trouver {target} dans le plan" -#: lib/App/Sqitch/Engine.pm:451 +#: lib/App/Sqitch/Engine.pm:454 #, fuzzy, perl-brace-format msgid "Change \"{change}\" is deployed, but not planned" msgstr "" "Le changement \"{change}\" duplique une dรฉclaration prรฉcรฉdente ร  la ligne " "{line}" -#: lib/App/Sqitch/Engine.pm:495 +#: lib/App/Sqitch/Engine.pm:498 msgid "Out of order" msgstr "" -#: lib/App/Sqitch/Engine.pm:501 +#: lib/App/Sqitch/Engine.pm:504 #, fuzzy msgid "Not present in the plan" msgstr "Impossible de trouver {target} dans le plan" -#: lib/App/Sqitch/Engine.pm:512 lib/App/Sqitch/Engine.pm:524 -#: lib/App/Sqitch/Engine.pm:993 lib/App/Sqitch/Engine.pm:1023 +#: lib/App/Sqitch/Engine.pm:515 lib/App/Sqitch/Engine.pm:527 +#: lib/App/Sqitch/Engine.pm:996 lib/App/Sqitch/Engine.pm:1026 msgid "not ok" msgstr "" -#: lib/App/Sqitch/Engine.pm:512 lib/App/Sqitch/Engine.pm:971 -#: lib/App/Sqitch/Engine.pm:1013 +#: lib/App/Sqitch/Engine.pm:515 lib/App/Sqitch/Engine.pm:974 +#: lib/App/Sqitch/Engine.pm:1016 msgid "ok" msgstr "" -#: lib/App/Sqitch/Engine.pm:526 +#: lib/App/Sqitch/Engine.pm:529 #, fuzzy msgid "Not deployed" msgstr "Pas de changements dรฉployรฉs" -#: lib/App/Sqitch/Engine.pm:558 +#: lib/App/Sqitch/Engine.pm:561 #, perl-brace-format msgid "Verify script \"{script}\" failed." msgstr "" -#: lib/App/Sqitch/Engine.pm:567 +#: lib/App/Sqitch/Engine.pm:570 #, fuzzy, perl-brace-format msgid "Verify script {file} does not exist" msgstr "Le fichier de plan {file} n'existe pas" -#: lib/App/Sqitch/Engine.pm:609 +#: lib/App/Sqitch/Engine.pm:612 #, perl-brace-format msgid "Conflicts with previously deployed change: {changes}" msgid_plural "Conflicts with previously deployed changes: {changes}" msgstr[0] "Conflits avec le changement dรฉployรฉ prรฉcรฉdemment : {changes}" msgstr[1] "Conflits avec les changements dรฉployรฉs prรฉcรฉdemment : {changes}" -#: lib/App/Sqitch/Engine.pm:616 +#: lib/App/Sqitch/Engine.pm:619 #, perl-brace-format msgid "Missing required change: {changes}" msgid_plural "Missing required changes: {changes}" msgstr[0] "Changement requis manquant : {changes}" msgstr[1] "Changements requis manquant : {changes}" -#: lib/App/Sqitch/Engine.pm:628 +#: lib/App/Sqitch/Engine.pm:631 #, perl-brace-format msgid "Change \"{changes}\" has already been deployed" msgid_plural "Changes have already been deployed: {changes}" msgstr[0] "" msgstr[1] "" -#: lib/App/Sqitch/Engine.pm:651 +#: lib/App/Sqitch/Engine.pm:654 #, fuzzy, perl-brace-format msgid "Change \"{change}\" required by currently deployed change: {changes}" msgid_plural "" @@ -892,139 +863,139 @@ msgid_plural "" msgstr[0] "Requis par le changement actuellement dรฉployรฉ : {changes}" msgstr[1] "Requis par les changements actuellement dรฉployรฉs : {changes}" -#: lib/App/Sqitch/Engine.pm:674 +#: lib/App/Sqitch/Engine.pm:677 #, perl-brace-format msgid "Invalid dependency: {dependency}" msgstr "Dรฉpendance invalide {dependency}" -#: lib/App/Sqitch/Engine.pm:810 lib/App/Sqitch/Plan/ChangeList.pm:88 +#: lib/App/Sqitch/Engine.pm:813 lib/App/Sqitch/Plan/ChangeList.pm:88 #, perl-brace-format msgid "" "Change \"{change}\" is ambiguous. Please specify a tag-qualified change:" msgstr "" -#: lib/App/Sqitch/Engine.pm:825 +#: lib/App/Sqitch/Engine.pm:828 msgid "Change Lookup Failed" msgstr "" -#: lib/App/Sqitch/Engine.pm:846 +#: lib/App/Sqitch/Engine.pm:849 #, fuzzy, perl-brace-format msgid "Reverting to {change}" msgstr "Annulation et retour vers {target}" -#: lib/App/Sqitch/Engine.pm:847 +#: lib/App/Sqitch/Engine.pm:850 msgid "Reverting all changes" msgstr "Annulation de tous les changements" -#: lib/App/Sqitch/Engine.pm:855 +#: lib/App/Sqitch/Engine.pm:858 msgid "The schema will need to be manually repaired" msgstr "Le schรฉma devra รชtre rรฉparรฉ manuellement" -#: lib/App/Sqitch/Engine.pm:859 lib/App/Sqitch/Engine.pm:987 +#: lib/App/Sqitch/Engine.pm:862 lib/App/Sqitch/Engine.pm:990 msgid "Deploy failed" msgstr "Echec du dรฉploiement" -#: lib/App/Sqitch/Engine.pm:916 +#: lib/App/Sqitch/Engine.pm:919 #, fuzzy, perl-brace-format msgid "Cannot find change {id} ({change}) in {file}" msgstr "Impossible de trouver ce changement dans {file}" -#: lib/App/Sqitch/Engine.pm:1038 +#: lib/App/Sqitch/Engine.pm:1041 #, perl-brace-format msgid "" "Blocked by another instance of Sqitch working on {dest}; waiting {secs} " "seconds..." msgstr "" -#: lib/App/Sqitch/Engine.pm:1048 +#: lib/App/Sqitch/Engine.pm:1051 #, perl-brace-format msgid "" "Timed out waiting {secs} seconds for another instance of Sqitch to finish " "work on {dest}" msgstr "" -#: lib/App/Sqitch/Engine.pm:1108 +#: lib/App/Sqitch/Engine.pm:1110 #, perl-brace-format msgid "No registry found in {destination}. Have you ever deployed?" msgstr "" -#: lib/App/Sqitch/Engine.pm:1113 +#: lib/App/Sqitch/Engine.pm:1115 #, perl-brace-format msgid "" "Registry version is {old} but {new} is the latest known. Please upgrade " "Sqitch" msgstr "" -#: lib/App/Sqitch/Engine.pm:1119 +#: lib/App/Sqitch/Engine.pm:1121 #, perl-brace-format msgid "" "Registry is at version {old} but latest is {new}. Please run the \"upgrade\" " "command" msgstr "" -#: lib/App/Sqitch/Engine.pm:1134 +#: lib/App/Sqitch/Engine.pm:1136 #, perl-brace-format msgid "" "Registry version is {old} but {new} is the latest known. Please upgrade " "Sqitch." msgstr "" -#: lib/App/Sqitch/Engine.pm:1149 +#: lib/App/Sqitch/Engine.pm:1151 #, perl-brace-format msgid "Cannot upgrade to {version}: Cannot find upgrade script \"{file}\"" msgstr "" -#: lib/App/Sqitch/Engine.pm:1156 +#: lib/App/Sqitch/Engine.pm:1158 #, fuzzy, perl-brace-format msgid "Upgrading the Sqitch registry from {old} to {new}" msgstr "Ajout des tables de metadonnรฉes ร  {destination}" -#: lib/App/Sqitch/Engine.pm:1163 +#: lib/App/Sqitch/Engine.pm:1165 #, perl-brace-format msgid "From {old} to {new}" msgstr "" -#: lib/App/Sqitch/Engine.pm:1208 +#: lib/App/Sqitch/Engine.pm:1210 #, fuzzy, perl-brace-format msgid "Checking {destination}" msgstr "Dรฉploiement des changements vers {destination}" -#: lib/App/Sqitch/Engine.pm:1215 +#: lib/App/Sqitch/Engine.pm:1217 #, fuzzy msgid "Nothing to check (no planned or deployed changes)" msgstr "Rien ร  annuler (rien de dรฉployรฉ)" -#: lib/App/Sqitch/Engine.pm:1228 +#: lib/App/Sqitch/Engine.pm:1230 #, fuzzy, perl-brace-format msgid "Script signatures diverge at change {change}" msgstr "Changement requis manquant : {changes}" -#: lib/App/Sqitch/Engine.pm:1236 +#: lib/App/Sqitch/Engine.pm:1238 #, perl-brace-format msgid "Failed one check" msgid_plural "Failed {count} checks" msgstr[0] "" msgstr[1] "" -#: lib/App/Sqitch/Engine.pm:1244 +#: lib/App/Sqitch/Engine.pm:1246 msgid "Check successful" msgstr "" -#: lib/App/Sqitch/Engine/exasol.pm:324 lib/App/Sqitch/Engine/oracle.pm:455 +#: lib/App/Sqitch/Engine/exasol.pm:324 lib/App/Sqitch/Engine/oracle.pm:458 msgid "Sqitch already initialized" msgstr "" -#: lib/App/Sqitch/Engine/exasol.pm:392 lib/App/Sqitch/Engine/oracle.pm:585 +#: lib/App/Sqitch/Engine/exasol.pm:392 lib/App/Sqitch/Engine/oracle.pm:588 #, fuzzy, perl-brace-format msgid "Cannot remove {file}: {error}" msgstr "Impossible d'ouvrir {file} : {error}" -#: lib/App/Sqitch/Engine/exasol.pm:401 lib/App/Sqitch/Engine/oracle.pm:594 +#: lib/App/Sqitch/Engine/exasol.pm:401 lib/App/Sqitch/Engine/oracle.pm:597 #, fuzzy, perl-brace-format msgid "Cannot copy {file} to {alias}: {error}" msgstr "Impossible de copier {src} vers {dest} : {error}" -#: lib/App/Sqitch/Engine/exasol.pm:410 lib/App/Sqitch/Engine/oracle.pm:603 +#: lib/App/Sqitch/Engine/exasol.pm:410 lib/App/Sqitch/Engine/oracle.pm:606 #, fuzzy, perl-brace-format msgid "Cannot symlink {file} to {alias}: {error}" msgstr "Impossible d'ouvrir {file} : {error}" @@ -1034,34 +1005,34 @@ msgstr "Impossible d'ouvrir {file} : {error}" msgid "{command} unexpectedly failed; exit value = {exitval}" msgstr "{command} a retournรฉ de maniรจre inattendue la valeur {exitval}" -#: lib/App/Sqitch/Engine/firebird.pm:207 lib/App/Sqitch/Engine/mysql.pm:274 +#: lib/App/Sqitch/Engine/firebird.pm:208 lib/App/Sqitch/Engine/mysql.pm:281 #: lib/App/Sqitch/Engine/sqlite.pm:158 #, perl-brace-format msgid "Sqitch database {database} already initialized" msgstr "" -#: lib/App/Sqitch/Engine/firebird.pm:226 +#: lib/App/Sqitch/Engine/firebird.pm:227 #, fuzzy, perl-brace-format msgid "Cannot create database {database}: {error}" msgstr "Impossible de copier {src} vers {dest} : {error}" -#: lib/App/Sqitch/Engine/firebird.pm:240 lib/App/Sqitch/Engine/sqlite.pm:127 +#: lib/App/Sqitch/Engine/firebird.pm:241 lib/App/Sqitch/Engine/sqlite.pm:127 #, perl-brace-format msgid "Database name missing in URI {uri}" msgstr "" -#: lib/App/Sqitch/Engine/firebird.pm:881 lib/App/Sqitch/Engine/firebird.pm:898 -#: lib/App/Sqitch/Engine/firebird.pm:909 +#: lib/App/Sqitch/Engine/firebird.pm:889 lib/App/Sqitch/Engine/firebird.pm:907 +#: lib/App/Sqitch/Engine/firebird.pm:918 #, fuzzy, perl-brace-format msgid "Cannot dup STDERR: {error}" msgstr "Impossible d'ouvrir {file} : {error}" -#: lib/App/Sqitch/Engine/firebird.pm:885 +#: lib/App/Sqitch/Engine/firebird.pm:893 #, fuzzy, perl-brace-format msgid "Cannot reirect STDERR: {error}" msgstr "Impossible d'exรฉcuter {command} : {error}" -#: lib/App/Sqitch/Engine/firebird.pm:912 +#: lib/App/Sqitch/Engine/firebird.pm:921 msgid "" "Unable to locate Firebird ISQL; set \"engine.firebird.client\" via sqitch " "config" @@ -1078,17 +1049,17 @@ msgstr "" msgid "Database name missing in URI \"{uri}\"" msgstr "" -#: lib/App/Sqitch/Engine/pg.pm:189 lib/App/Sqitch/Engine/snowflake.pm:300 +#: lib/App/Sqitch/Engine/pg.pm:212 lib/App/Sqitch/Engine/snowflake.pm:300 #: lib/App/Sqitch/Engine/vertica.pm:142 #, perl-brace-format msgid "Sqitch schema \"{schema}\" already exists" msgstr "Le schรฉma Sqitch \"{schema}\" existe dรฉjร " -#: lib/App/Sqitch/Engine/pg.pm:412 +#: lib/App/Sqitch/Engine/pg.pm:452 msgid "Sqitch registry not initialized" msgstr "" -#: lib/App/Sqitch/Engine/pg.pm:413 +#: lib/App/Sqitch/Engine/pg.pm:453 msgid "" "Because the \"changes\" table does not exist, Sqitch will now initialize the " "database to create its registry tables." @@ -1560,6 +1531,10 @@ msgstr "Le nom d'utilisateur ne peut contenir \"<\" ou commenรงant par \"[\"" msgid "User email may not contain \">\"" msgstr "L'email utilisateur ne peut contenir \">\"" +#, fuzzy, perl-brace-format +#~ msgid "Loading {file}" +#~ msgstr "{file} crรฉe" + #, fuzzy #~ msgid "Updating legacy change and tag IDs in {destination}" #~ msgstr "Annulation de tous les changements de {destination}" diff --git a/po/it_IT.po b/po/it_IT.po index f434d0839..e1397fbdd 100644 --- a/po/it_IT.po +++ b/po/it_IT.po @@ -1,5 +1,5 @@ # Sqitch Italian Localization Messages -# Copyright (c) 2012-2021 iovation Inc., David E. Wheeler +# Copyright (c) 2012-2022 iovation Inc., David E. Wheeler # This file is distributed under the same license as the App-Sqitch package. # Luca Ferrari , 2017 # @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: App-Sqitch 0.9996\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-11-14 19:00-0500\n" +"POT-Creation-Date: 2022-08-12 16:04-0400\n" "PO-Revision-Date: 2017-10-12 10:30+0200\n" "Last-Translator: Luca Ferrari \n" "Language-Team: Italian \n" @@ -32,93 +32,106 @@ msgstr "" "Non riesco a risalire alla tua e-mail: esegui sqitch config --user user." "email tuaemail@host.com" -#: lib/App/Sqitch.pm:282 +#: lib/App/Sqitch.pm:280 #, fuzzy, perl-brace-format msgid "Cannot change to directory {directory}: {error}" msgstr "Non posso copiare {src} in {dest}: {error}" -#: lib/App/Sqitch.pm:316 lib/App/Sqitch/Command.pm:112 +#: lib/App/Sqitch.pm:314 lib/App/Sqitch/Command.pm:113 #, perl-brace-format msgid "\"{command}\" is not a valid command" msgstr "\"{command}\" non รจ un comando valido" -#: lib/App/Sqitch.pm:410 +#: lib/App/Sqitch.pm:408 msgid "" "Sqitch seems to be unattended and there is no default value for this question" msgstr "" "Sqitch sembra non presidiato e non ha un valore di default per il quesito" -#: lib/App/Sqitch.pm:429 +#: lib/App/Sqitch.pm:427 msgctxt "Confirm prompt answer yes" msgid "Yes" msgstr "" -#: lib/App/Sqitch.pm:430 +#: lib/App/Sqitch.pm:428 msgctxt "Confirm prompt answer no" msgid "No" msgstr "" -#: lib/App/Sqitch.pm:439 +#: lib/App/Sqitch.pm:437 msgid "Please answer \"y\" or \"n\"." msgstr "Rispondi \"y\" (si) o \"n\" (no)" -#: lib/App/Sqitch.pm:442 +#: lib/App/Sqitch.pm:440 msgid "No valid answer after 3 attempts; aborting" msgstr "Nessuna risposta valida in 3 tentativi, aborto" -#: lib/App/Sqitch.pm:463 lib/App/Sqitch.pm:470 +#: lib/App/Sqitch.pm:461 lib/App/Sqitch.pm:468 #, perl-brace-format msgid "Cannot exec {command}: {error}" msgstr "Non posso eseguire {command}: {error}" -#: lib/App/Sqitch.pm:486 +#: lib/App/Sqitch.pm:484 #, perl-brace-format msgid "Error closing pipe to {command}: {error}" msgstr "Errore di chiusura pipe per {command}: {error}" -#: lib/App/Sqitch.pm:490 lib/App/Sqitch/Engine/oracle.pm:758 +#: lib/App/Sqitch.pm:488 lib/App/Sqitch/Engine/oracle.pm:761 #, perl-brace-format msgid "{command} unexpectedly returned exit value {exitval}" msgstr "Il comando {command} ha restituito un exit value inatteso {exitval}" -#: lib/App/Sqitch/Command.pm:282 +#: lib/App/Sqitch/Command.pm:283 #, perl-brace-format msgid "Unknown argument \"{arg}\"" msgid_plural "Unknown arguments: {arg}" msgstr[0] "Argomento sconosciuto: \"{arg}\"" msgstr[1] "Argomenti sconosciuti: {arg}" -#: lib/App/Sqitch/Command.pm:294 +#: lib/App/Sqitch/Command.pm:295 msgid "Cannot specify both --all and engine, target, or plan arugments" msgstr "" "Non si puรฒ specificare contemporaneamente --all e target, target o plan." -#: lib/App/Sqitch/Command/add.pm:103 +#: lib/App/Sqitch/Command.pm:315 lib/App/Sqitch/Command/add.pm:410 +#: lib/App/Sqitch/Command/init.pm:203 +#: lib/App/Sqitch/Role/TargetConfigCommand.pm:250 +#: lib/App/Sqitch/Role/TargetConfigCommand.pm:337 +#, perl-brace-format +msgid "Created {file}" +msgstr "{file} creato" + +#: lib/App/Sqitch/Command.pm:322 lib/App/Sqitch/Role/TargetConfigCommand.pm:256 +#, perl-brace-format +msgid "Error creating {path}: {error}" +msgstr "Errore durante la creazione di {path}: {error}" + +#: lib/App/Sqitch/Command/add.pm:102 #, perl-brace-format msgid "Template {template} does not exist" msgstr "Il template {template} non esiste" -#: lib/App/Sqitch/Command/add.pm:108 +#: lib/App/Sqitch/Command/add.pm:107 #, perl-brace-format msgid "Template {template} is not a file" msgstr "Il template {template} non รจ un file" -#: lib/App/Sqitch/Command/add.pm:146 +#: lib/App/Sqitch/Command/add.pm:145 #, perl-brace-format msgid "Cannot find {script} template" msgstr "Non trovo il template {script}" -#: lib/App/Sqitch/Command/add.pm:225 +#: lib/App/Sqitch/Command/add.pm:224 #, perl-brace-format msgid "Directory \"{dir}\" does not exist" msgstr "La directory {dir} non esiste" -#: lib/App/Sqitch/Command/add.pm:230 +#: lib/App/Sqitch/Command/add.pm:229 #, perl-brace-format msgid "\"{dir}\" is not a directory" msgstr "\"{dir}\" non รจ una directory" -#: lib/App/Sqitch/Command/add.pm:283 +#: lib/App/Sqitch/Command/add.pm:282 #, perl-brace-format msgid "" "Name \"{name}\" identifies a target; use \"--change {name}\" to use it for " @@ -127,27 +140,21 @@ msgstr "" "Il nome \"{name}\" identifica un target, usa \"--change {name}\" per " "effettuare il cambio di nome" -#: lib/App/Sqitch/Command/add.pm:331 +#: lib/App/Sqitch/Command/add.pm:330 msgid "add" msgstr "aggiungi" -#: lib/App/Sqitch/Command/add.pm:349 +#: lib/App/Sqitch/Command/add.pm:348 #, perl-brace-format msgid "Added \"{change}\" to {file}" msgstr "\"{change}\" aggiunta al file {file}" -#: lib/App/Sqitch/Command/add.pm:368 +#: lib/App/Sqitch/Command/add.pm:367 #, perl-brace-format msgid "Skipped {file}: already exists" msgstr "Saltato {file}: esiste giร " -#: lib/App/Sqitch/Command/add.pm:379 lib/App/Sqitch/Command/bundle.pm:141 -#: lib/App/Sqitch/Role/TargetConfigCommand.pm:256 -#, perl-brace-format -msgid "Error creating {path}: {error}" -msgstr "Errore durante la creazione di {path}: {error}" - -#: lib/App/Sqitch/Command/add.pm:396 lib/App/Sqitch/Command/add.pm:426 +#: lib/App/Sqitch/Command/add.pm:386 lib/App/Sqitch/Command/add.pm:416 #: lib/App/Sqitch/Plan.pm:134 lib/App/Sqitch/Plan.pm:591 #: lib/App/Sqitch/Plan.pm:972 lib/App/Sqitch/Plan/Line.pm:107 #: lib/App/Sqitch/Role/TargetConfigCommand.pm:321 @@ -155,25 +162,17 @@ msgstr "Errore durante la creazione di {path}: {error}" msgid "Cannot open {file}: {error}" msgstr "Non posso aprire {file}: {error}" -#: lib/App/Sqitch/Command/add.pm:404 +#: lib/App/Sqitch/Command/add.pm:394 #, perl-brace-format msgid "Error executing {template}: {error}" msgstr "Errore nell'esecuzione di {template}: {error}" -#: lib/App/Sqitch/Command/add.pm:416 +#: lib/App/Sqitch/Command/add.pm:406 #: lib/App/Sqitch/Role/TargetConfigCommand.pm:332 #, perl-brace-format msgid "Error closing {file}: {error}" msgstr "Errore nella chiusura di {file}: {error}" -#: lib/App/Sqitch/Command/add.pm:420 lib/App/Sqitch/Command/bundle.pm:134 -#: lib/App/Sqitch/Command/init.pm:203 -#: lib/App/Sqitch/Role/TargetConfigCommand.pm:250 -#: lib/App/Sqitch/Role/TargetConfigCommand.pm:337 -#, perl-brace-format -msgid "Created {file}" -msgstr "{file} creato" - #: lib/App/Sqitch/Command/bundle.pm:99 msgid "" "Use of --to or --from to bundle multiple targets is not recommended.\n" @@ -192,41 +191,41 @@ msgstr "" msgid "Bundling into {dir}" msgstr "Creazione bundle in {dir}" -#: lib/App/Sqitch/Command/bundle.pm:152 +#: lib/App/Sqitch/Command/bundle.pm:136 #, perl-brace-format msgid "Cannot copy {file}: does not exist" msgstr "Non posso copiare {file}: non esiste" -#: lib/App/Sqitch/Command/bundle.pm:165 +#: lib/App/Sqitch/Command/bundle.pm:149 #, perl-brace-format msgid "Copying {source} -> {dest}" msgstr "Copio {source} -> {dest}" -#: lib/App/Sqitch/Command/bundle.pm:172 +#: lib/App/Sqitch/Command/bundle.pm:156 #, perl-brace-format msgid "Cannot copy \"{source}\" to \"{dest}\": {error}" msgstr "Non posso copiare \"{source}\" in \"{dest}\": {error}" -#: lib/App/Sqitch/Command/bundle.pm:182 +#: lib/App/Sqitch/Command/bundle.pm:166 msgid "Writing config" msgstr "Salvo configurazione" -#: lib/App/Sqitch/Command/bundle.pm:193 +#: lib/App/Sqitch/Command/bundle.pm:177 msgid "Writing plan" msgstr "Salvo piano esecuzione (plan)" -#: lib/App/Sqitch/Command/bundle.pm:202 +#: lib/App/Sqitch/Command/bundle.pm:186 #, perl-brace-format msgid "Writing plan from {from} to {to}" msgstr "Salvo piano da {from} a {to}" -#: lib/App/Sqitch/Command/bundle.pm:222 lib/App/Sqitch/Command/bundle.pm:229 +#: lib/App/Sqitch/Command/bundle.pm:206 lib/App/Sqitch/Command/bundle.pm:213 #: lib/App/Sqitch/Plan.pm:944 lib/App/Sqitch/Plan.pm:953 #, perl-brace-format msgid "Cannot find change {change}" msgstr "Non trovo la modifica {change}" -#: lib/App/Sqitch/Command/bundle.pm:233 +#: lib/App/Sqitch/Command/bundle.pm:217 msgid "Writing scripts" msgstr "Salvo gli script" @@ -380,38 +379,6 @@ msgstr "" msgid "No Variables" msgstr "" -#: lib/App/Sqitch/Command/engine.pm:253 -#, perl-brace-format -msgid "Loading {file}" -msgstr "Caricamento di {file}" - -#: lib/App/Sqitch/Command/engine.pm:266 -#, perl-brace-format -msgid "" -"Deprecated {section} found in {file}; to remove it, run\n" -" {sqitch} config --file {file} --remove-section {section}" -msgstr "" -"Sezione {section} nel file {file} รจ deprecata; per rimuoverla esegui\n" -" {sqitch} config --file {file} --remove-section {section}" - -#: lib/App/Sqitch/Command/engine.pm:278 -msgid " - No engines to update" -msgstr " - Nessun motore da aggiornare" - -#: lib/App/Sqitch/Command/engine.pm:285 -#, perl-brace-format -msgid "Cannot update {file}. Please make it writable" -msgstr "Impossibile aggiornare il file {file}. Rendilo scrivibile" - -#: lib/App/Sqitch/Command/engine.pm:341 -#, perl-brace-format -msgid "" -"Migrated {old} to {new}; To remove {old}, run\n" -" {sqitch} config --file {file} --remove-section {old}" -msgstr "" -"MIgrazioIne da {old} a {new} completata. Per rimuovere {old} esegui\n" -" {sqitch} config --file {file} --remove-section {old}" - #: lib/App/Sqitch/Command/help.pm:46 #, perl-brace-format msgid "No manual entry for {command}" @@ -454,7 +421,7 @@ msgstr "Sul database {db}" msgid "Unknown plan format \"{format}\"" msgstr "Formato \"{format}\" del piano di esecuzione sconosciuto " -#: lib/App/Sqitch/Command/plan.pm:199 lib/App/Sqitch/Command/upgrade.pm:40 +#: lib/App/Sqitch/Command/plan.pm:199 lib/App/Sqitch/Command/upgrade.pm:39 #, perl-brace-format msgid "Too many targets specified; using {target}" msgstr "Troppi target specificati; si usa {target}" @@ -558,8 +525,8 @@ msgstr "Usa --project per selezionare quale progetto interrogare: {projects}" msgid ", " msgstr ", " -#: lib/App/Sqitch/Command/status.pm:138 lib/App/Sqitch/Engine.pm:381 -#: lib/App/Sqitch/Engine.pm:1214 +#: lib/App/Sqitch/Command/status.pm:138 lib/App/Sqitch/Engine.pm:384 +#: lib/App/Sqitch/Engine.pm:1216 msgid "No changes deployed" msgstr "Nessuna modifica implementata" @@ -627,11 +594,11 @@ msgid "Make sure you are connected to the proper database for this project." msgstr "" "Assicurati di essere collegato al database corretto per questo progetto." -#: lib/App/Sqitch/Command/status.pm:308 lib/App/Sqitch/Engine.pm:216 +#: lib/App/Sqitch/Command/status.pm:308 lib/App/Sqitch/Engine.pm:217 msgid "Nothing to deploy (up-to-date)" msgstr "Niente da implementare (tutto giร  aggiornato)" -#: lib/App/Sqitch/Command/status.pm:311 lib/App/Sqitch/Engine.pm:536 +#: lib/App/Sqitch/Command/status.pm:311 lib/App/Sqitch/Engine.pm:539 msgid "Undeployed change:" msgid_plural "Undeployed changes:" msgstr[0] "Modifica rimosse dalla implementazione:" @@ -676,12 +643,12 @@ msgstr "" msgid "URI" msgstr "URI" -#: lib/App/Sqitch/Command/upgrade.pm:48 +#: lib/App/Sqitch/Command/upgrade.pm:47 #, perl-brace-format msgid "Upgrading registry {registry} to version {version}" msgstr "Aggiorno il registro {registry} alla versione {version}" -#: lib/App/Sqitch/Command/upgrade.pm:55 +#: lib/App/Sqitch/Command/upgrade.pm:54 #, perl-brace-format msgid "Registry {registry} is up-to-date at version {version}" msgstr "Il registro {registry} รจ aggiornato alla versione {version}" @@ -711,166 +678,166 @@ msgstr "Nessun motore specificato: usa --engine o imposta core.engine" msgid "{driver} required to manage {engine}" msgstr "{driver} รจ necessario per gestire {engine}" -#: lib/App/Sqitch/Engine.pm:196 +#: lib/App/Sqitch/Engine.pm:197 msgid "Nothing to deploy (empty plan)" msgstr "Niente da implementare (piano di esecuzione vuoto)" -#: lib/App/Sqitch/Engine.pm:200 lib/App/Sqitch/Engine.pm:294 +#: lib/App/Sqitch/Engine.pm:201 lib/App/Sqitch/Engine.pm:297 #: lib/App/Sqitch/Plan.pm:738 lib/App/Sqitch/Plan/ChangeList.pm:121 #, perl-brace-format msgid "Unknown change: \"{change}\"" msgstr "Modifica \"{change}\" sconosciuta" -#: lib/App/Sqitch/Engine.pm:207 +#: lib/App/Sqitch/Engine.pm:208 #, perl-brace-format msgid "Nothing to deploy (already at \"{change}\")" msgstr "Niente da implementare (giร  a \"{change}\")" -#: lib/App/Sqitch/Engine.pm:225 +#: lib/App/Sqitch/Engine.pm:226 #, perl-brace-format msgid "Adding registry tables to {destination}" msgstr "Aggiungo le tabelle di registro a {destination}" -#: lib/App/Sqitch/Engine.pm:234 +#: lib/App/Sqitch/Engine.pm:235 msgid "Cannot deploy to an earlier change; use \"revert\" instead" msgstr "Non posso implementare una modifica precedente; usa \"revert\"." -#: lib/App/Sqitch/Engine.pm:242 +#: lib/App/Sqitch/Engine.pm:243 #, perl-brace-format msgid "Deploying changes through {change} to {destination}" msgstr "Implementazione delle modifiche da {change} a {destination}" -#: lib/App/Sqitch/Engine.pm:246 +#: lib/App/Sqitch/Engine.pm:247 #, perl-brace-format msgid "Deploying changes to {destination}" msgstr "Implementazione delle modifiche a {destination}" -#: lib/App/Sqitch/Engine.pm:259 +#: lib/App/Sqitch/Engine.pm:260 #, perl-brace-format msgid "Unknown deployment mode: \"{mode}\"" msgstr "Modalitร  di implementazione \"{mode}\" sconosciuta" -#: lib/App/Sqitch/Engine.pm:288 +#: lib/App/Sqitch/Engine.pm:291 #, perl-brace-format msgid "Change not deployed: \"{change}\"" msgstr "Modifica non implementata: \"{change}\"" -#: lib/App/Sqitch/Engine.pm:303 +#: lib/App/Sqitch/Engine.pm:306 #, perl-brace-format msgid "No changes deployed since: \"{change}\"" msgstr "Nessuna modifica implementata da: \"{change}\"" -#: lib/App/Sqitch/Engine.pm:311 +#: lib/App/Sqitch/Engine.pm:314 #, perl-brace-format msgid "Reverting changes to {change} from {destination}" msgstr "Annullo modifiche da {destination} a {change}" -#: lib/App/Sqitch/Engine.pm:318 lib/App/Sqitch/Engine.pm:341 +#: lib/App/Sqitch/Engine.pm:321 lib/App/Sqitch/Engine.pm:344 msgid "Nothing reverted" msgstr "Niente รจ stato annullato" -#: lib/App/Sqitch/Engine.pm:321 +#: lib/App/Sqitch/Engine.pm:324 #, perl-brace-format msgid "Revert changes to {change} from {destination}?" msgstr "Annullo modifiche da {destination} a {change}" -#: lib/App/Sqitch/Engine.pm:329 +#: lib/App/Sqitch/Engine.pm:332 msgid "Nothing to revert (nothing deployed)" msgstr "Niente da annullare (niente da implementare)" -#: lib/App/Sqitch/Engine.pm:335 +#: lib/App/Sqitch/Engine.pm:338 #, perl-brace-format msgid "Reverting all changes from {destination}" msgstr "Annullo tutte le modifiche da {destination}" -#: lib/App/Sqitch/Engine.pm:344 +#: lib/App/Sqitch/Engine.pm:347 #, perl-brace-format msgid "Revert all changes from {destination}?" msgstr "Annullo tutte le modifiche da {destination}?" -#: lib/App/Sqitch/Engine.pm:375 +#: lib/App/Sqitch/Engine.pm:378 #, perl-brace-format msgid "Verifying {destination}" msgstr "Veifico {destination}" -#: lib/App/Sqitch/Engine.pm:382 +#: lib/App/Sqitch/Engine.pm:385 msgid "Nothing to verify (no planned or deployed changes)" msgstr "Niente da verificare (nessuna modifica pianificata o implementata)" -#: lib/App/Sqitch/Engine.pm:389 +#: lib/App/Sqitch/Engine.pm:392 msgid "There are deployed changes, but none planned!" msgstr "Ci sono modifiche implementate, ma nessuna pianificata!" -#: lib/App/Sqitch/Engine.pm:402 +#: lib/App/Sqitch/Engine.pm:405 msgid "Verify Summary Report" msgstr "Report Sommario di verifica" -#: lib/App/Sqitch/Engine.pm:405 +#: lib/App/Sqitch/Engine.pm:408 #, perl-brace-format msgid "Changes: {number}" msgstr "Modifiche: {number}" -#: lib/App/Sqitch/Engine.pm:406 +#: lib/App/Sqitch/Engine.pm:409 #, perl-brace-format msgid "Errors: {number}" msgstr "Errori: {number}" -#: lib/App/Sqitch/Engine.pm:407 +#: lib/App/Sqitch/Engine.pm:410 msgid "Verify failed" msgstr "Verifica fallita" -#: lib/App/Sqitch/Engine.pm:412 +#: lib/App/Sqitch/Engine.pm:415 msgid "Verify successful" msgstr "Verifica riuscita" -#: lib/App/Sqitch/Engine.pm:441 +#: lib/App/Sqitch/Engine.pm:444 #, perl-brace-format msgid "Change \"{change}\" has not been deployed" msgstr "Modifica \"{change}\" non implementata" -#: lib/App/Sqitch/Engine.pm:444 +#: lib/App/Sqitch/Engine.pm:447 #, perl-brace-format msgid "Cannot find \"{change}\" in the database or the plan" msgstr "Non posso trovare \"{change}\" nel database o nel piano di esecuzione" -#: lib/App/Sqitch/Engine.pm:451 +#: lib/App/Sqitch/Engine.pm:454 #, perl-brace-format msgid "Change \"{change}\" is deployed, but not planned" msgstr "La modifica \"{change}\" รจ implementata ma non pianificata" -#: lib/App/Sqitch/Engine.pm:495 +#: lib/App/Sqitch/Engine.pm:498 msgid "Out of order" msgstr "Fuori servizio" -#: lib/App/Sqitch/Engine.pm:501 +#: lib/App/Sqitch/Engine.pm:504 msgid "Not present in the plan" msgstr "Non presente nel piano di esecuzione" -#: lib/App/Sqitch/Engine.pm:512 lib/App/Sqitch/Engine.pm:524 -#: lib/App/Sqitch/Engine.pm:993 lib/App/Sqitch/Engine.pm:1023 +#: lib/App/Sqitch/Engine.pm:515 lib/App/Sqitch/Engine.pm:527 +#: lib/App/Sqitch/Engine.pm:996 lib/App/Sqitch/Engine.pm:1026 msgid "not ok" msgstr "Non OK" -#: lib/App/Sqitch/Engine.pm:512 lib/App/Sqitch/Engine.pm:971 -#: lib/App/Sqitch/Engine.pm:1013 +#: lib/App/Sqitch/Engine.pm:515 lib/App/Sqitch/Engine.pm:974 +#: lib/App/Sqitch/Engine.pm:1016 msgid "ok" msgstr "OK" -#: lib/App/Sqitch/Engine.pm:526 +#: lib/App/Sqitch/Engine.pm:529 msgid "Not deployed" msgstr "Non implementato" -#: lib/App/Sqitch/Engine.pm:558 +#: lib/App/Sqitch/Engine.pm:561 #, perl-brace-format msgid "Verify script \"{script}\" failed." msgstr "Script di verifica \"{script}\" fallito" -#: lib/App/Sqitch/Engine.pm:567 +#: lib/App/Sqitch/Engine.pm:570 #, perl-brace-format msgid "Verify script {file} does not exist" msgstr "Lo script di verifica {file} non esiste" -#: lib/App/Sqitch/Engine.pm:609 +#: lib/App/Sqitch/Engine.pm:612 #, perl-brace-format msgid "Conflicts with previously deployed change: {changes}" msgid_plural "Conflicts with previously deployed changes: {changes}" @@ -879,21 +846,21 @@ msgstr[0] "" msgstr[1] "" "Ci sono conflitti con le modifiche precededentemente implementate: {changes}" -#: lib/App/Sqitch/Engine.pm:616 +#: lib/App/Sqitch/Engine.pm:619 #, perl-brace-format msgid "Missing required change: {changes}" msgid_plural "Missing required changes: {changes}" msgstr[0] "Modifica richiesta non trovata: {changes}" msgstr[1] "Modifiche richieste non trovate: {changes}" -#: lib/App/Sqitch/Engine.pm:628 +#: lib/App/Sqitch/Engine.pm:631 #, perl-brace-format msgid "Change \"{changes}\" has already been deployed" msgid_plural "Changes have already been deployed: {changes}" msgstr[0] "La modifica \"{changes}\" รจ giร  stata implementata" msgstr[1] "Le modifiche \"{changes}\" sono giร  state implementate" -#: lib/App/Sqitch/Engine.pm:651 +#: lib/App/Sqitch/Engine.pm:654 #, perl-brace-format msgid "Change \"{change}\" required by currently deployed change: {changes}" msgid_plural "" @@ -905,66 +872,66 @@ msgstr[1] "" "La modifica \"{change}\" รจ richiesta dalle modifiche correntemente " "implementate: {changes}" -#: lib/App/Sqitch/Engine.pm:674 +#: lib/App/Sqitch/Engine.pm:677 #, perl-brace-format msgid "Invalid dependency: {dependency}" msgstr "Dipendenza {dependency} non valida" -#: lib/App/Sqitch/Engine.pm:810 lib/App/Sqitch/Plan/ChangeList.pm:88 +#: lib/App/Sqitch/Engine.pm:813 lib/App/Sqitch/Plan/ChangeList.pm:88 #, perl-brace-format msgid "" "Change \"{change}\" is ambiguous. Please specify a tag-qualified change:" msgstr "" "La modifica \"{change}\" รจ ambigua. Per favore indica la modifica con un tag." -#: lib/App/Sqitch/Engine.pm:825 +#: lib/App/Sqitch/Engine.pm:828 msgid "Change Lookup Failed" msgstr "Recupero della modifica fallito" -#: lib/App/Sqitch/Engine.pm:846 +#: lib/App/Sqitch/Engine.pm:849 #, perl-brace-format msgid "Reverting to {change}" msgstr "Ritorno a {change}" -#: lib/App/Sqitch/Engine.pm:847 +#: lib/App/Sqitch/Engine.pm:850 msgid "Reverting all changes" msgstr "Annullo tutte le modifiche" -#: lib/App/Sqitch/Engine.pm:855 +#: lib/App/Sqitch/Engine.pm:858 msgid "The schema will need to be manually repaired" msgstr "Lo schema richiede una riparazione manuale." -#: lib/App/Sqitch/Engine.pm:859 lib/App/Sqitch/Engine.pm:987 +#: lib/App/Sqitch/Engine.pm:862 lib/App/Sqitch/Engine.pm:990 msgid "Deploy failed" msgstr "Implementazione fallita" -#: lib/App/Sqitch/Engine.pm:916 +#: lib/App/Sqitch/Engine.pm:919 #, perl-brace-format msgid "Cannot find change {id} ({change}) in {file}" msgstr "Non trovo la modifica {id} ({change}) in {file}" -#: lib/App/Sqitch/Engine.pm:1038 +#: lib/App/Sqitch/Engine.pm:1041 #, perl-brace-format msgid "" "Blocked by another instance of Sqitch working on {dest}; waiting {secs} " "seconds..." msgstr "" -#: lib/App/Sqitch/Engine.pm:1048 +#: lib/App/Sqitch/Engine.pm:1051 #, perl-brace-format msgid "" "Timed out waiting {secs} seconds for another instance of Sqitch to finish " "work on {dest}" msgstr "" -#: lib/App/Sqitch/Engine.pm:1108 +#: lib/App/Sqitch/Engine.pm:1110 #, perl-brace-format msgid "No registry found in {destination}. Have you ever deployed?" msgstr "" "Nessun registro trovato in {destination}. E' mai stata fatta una " "implementazione prima?" -#: lib/App/Sqitch/Engine.pm:1113 +#: lib/App/Sqitch/Engine.pm:1115 #, perl-brace-format msgid "" "Registry version is {old} but {new} is the latest known. Please upgrade " @@ -973,7 +940,7 @@ msgstr "" "La versione di registro รจ {old} ma {new} รจ la piรน recente conosciuta. Per " "favore aggiorna Sqitch." -#: lib/App/Sqitch/Engine.pm:1119 +#: lib/App/Sqitch/Engine.pm:1121 #, perl-brace-format msgid "" "Registry is at version {old} but latest is {new}. Please run the \"upgrade\" " @@ -982,7 +949,7 @@ msgstr "" "La versione di registro รจ {old} ma {new} รจ la piรน recente conosciuta. Per " "favore esegui il comando \"upgrade\"." -#: lib/App/Sqitch/Engine.pm:1134 +#: lib/App/Sqitch/Engine.pm:1136 #, perl-brace-format msgid "" "Registry version is {old} but {new} is the latest known. Please upgrade " @@ -991,65 +958,65 @@ msgstr "" "La versione di registro รจ {old} ma {new} รจ la piรน recente conosciuta. Per " "favore aggiorna Sqitch." -#: lib/App/Sqitch/Engine.pm:1149 +#: lib/App/Sqitch/Engine.pm:1151 #, perl-brace-format msgid "Cannot upgrade to {version}: Cannot find upgrade script \"{file}\"" msgstr "" "Non posso aggiornare a {version}: non trovo lo script \"{file}\" per " "l'aggiornamento" -#: lib/App/Sqitch/Engine.pm:1156 +#: lib/App/Sqitch/Engine.pm:1158 #, fuzzy, perl-brace-format msgid "Upgrading the Sqitch registry from {old} to {new}" msgstr "Aggiorno il registro {registry} alla versione {version}" -#: lib/App/Sqitch/Engine.pm:1163 +#: lib/App/Sqitch/Engine.pm:1165 #, perl-brace-format msgid "From {old} to {new}" msgstr "Da {old} a {new}" -#: lib/App/Sqitch/Engine.pm:1208 +#: lib/App/Sqitch/Engine.pm:1210 #, fuzzy, perl-brace-format msgid "Checking {destination}" msgstr "Veifico {destination}" -#: lib/App/Sqitch/Engine.pm:1215 +#: lib/App/Sqitch/Engine.pm:1217 #, fuzzy msgid "Nothing to check (no planned or deployed changes)" msgstr "Niente da verificare (nessuna modifica pianificata o implementata)" -#: lib/App/Sqitch/Engine.pm:1228 +#: lib/App/Sqitch/Engine.pm:1230 #, fuzzy, perl-brace-format msgid "Script signatures diverge at change {change}" msgstr "Modifica richiesta non trovata: {changes}" -#: lib/App/Sqitch/Engine.pm:1236 +#: lib/App/Sqitch/Engine.pm:1238 #, perl-brace-format msgid "Failed one check" msgid_plural "Failed {count} checks" msgstr[0] "" msgstr[1] "" -#: lib/App/Sqitch/Engine.pm:1244 +#: lib/App/Sqitch/Engine.pm:1246 #, fuzzy msgid "Check successful" msgstr "Verifica riuscita" -#: lib/App/Sqitch/Engine/exasol.pm:324 lib/App/Sqitch/Engine/oracle.pm:455 +#: lib/App/Sqitch/Engine/exasol.pm:324 lib/App/Sqitch/Engine/oracle.pm:458 msgid "Sqitch already initialized" msgstr "Sqitch รจ giร  stato inizializzato" -#: lib/App/Sqitch/Engine/exasol.pm:392 lib/App/Sqitch/Engine/oracle.pm:585 +#: lib/App/Sqitch/Engine/exasol.pm:392 lib/App/Sqitch/Engine/oracle.pm:588 #, perl-brace-format msgid "Cannot remove {file}: {error}" msgstr "Non posso rimuovere {file}: {error}" -#: lib/App/Sqitch/Engine/exasol.pm:401 lib/App/Sqitch/Engine/oracle.pm:594 +#: lib/App/Sqitch/Engine/exasol.pm:401 lib/App/Sqitch/Engine/oracle.pm:597 #, perl-brace-format msgid "Cannot copy {file} to {alias}: {error}" msgstr "Non posso copiare {file} su {alias}: {error}" -#: lib/App/Sqitch/Engine/exasol.pm:410 lib/App/Sqitch/Engine/oracle.pm:603 +#: lib/App/Sqitch/Engine/exasol.pm:410 lib/App/Sqitch/Engine/oracle.pm:606 #, perl-brace-format msgid "Cannot symlink {file} to {alias}: {error}" msgstr "Non posso creare un link simbolico da {file} a {alias}: {error}" @@ -1059,34 +1026,34 @@ msgstr "Non posso creare un link simbolico da {file} a {alias}: {error}" msgid "{command} unexpectedly failed; exit value = {exitval}" msgstr "Il comando {command} ha restituito un exit value inatteso {exitval}" -#: lib/App/Sqitch/Engine/firebird.pm:207 lib/App/Sqitch/Engine/mysql.pm:274 +#: lib/App/Sqitch/Engine/firebird.pm:208 lib/App/Sqitch/Engine/mysql.pm:281 #: lib/App/Sqitch/Engine/sqlite.pm:158 #, perl-brace-format msgid "Sqitch database {database} already initialized" msgstr "Database Sqitch giร  inizialiatto su {database}" -#: lib/App/Sqitch/Engine/firebird.pm:226 +#: lib/App/Sqitch/Engine/firebird.pm:227 #, perl-brace-format msgid "Cannot create database {database}: {error}" msgstr "Non posso creare il database {database}: {error}" -#: lib/App/Sqitch/Engine/firebird.pm:240 lib/App/Sqitch/Engine/sqlite.pm:127 +#: lib/App/Sqitch/Engine/firebird.pm:241 lib/App/Sqitch/Engine/sqlite.pm:127 #, perl-brace-format msgid "Database name missing in URI {uri}" msgstr "Il nome di database non รจ specificato nell'URI {uri}" -#: lib/App/Sqitch/Engine/firebird.pm:881 lib/App/Sqitch/Engine/firebird.pm:898 -#: lib/App/Sqitch/Engine/firebird.pm:909 +#: lib/App/Sqitch/Engine/firebird.pm:889 lib/App/Sqitch/Engine/firebird.pm:907 +#: lib/App/Sqitch/Engine/firebird.pm:918 #, perl-brace-format msgid "Cannot dup STDERR: {error}" msgstr "Non posso effettuare la dup su STDERR: {error}" -#: lib/App/Sqitch/Engine/firebird.pm:885 +#: lib/App/Sqitch/Engine/firebird.pm:893 #, perl-brace-format msgid "Cannot reirect STDERR: {error}" msgstr "Non posso redirezionare STDERR: {error}" -#: lib/App/Sqitch/Engine/firebird.pm:912 +#: lib/App/Sqitch/Engine/firebird.pm:921 msgid "" "Unable to locate Firebird ISQL; set \"engine.firebird.client\" via sqitch " "config" @@ -1107,18 +1074,18 @@ msgstr "" msgid "Database name missing in URI \"{uri}\"" msgstr "Il nome di database non รจ specificato nell'URI {uri}" -#: lib/App/Sqitch/Engine/pg.pm:189 lib/App/Sqitch/Engine/snowflake.pm:300 +#: lib/App/Sqitch/Engine/pg.pm:212 lib/App/Sqitch/Engine/snowflake.pm:300 #: lib/App/Sqitch/Engine/vertica.pm:142 #, perl-brace-format msgid "Sqitch schema \"{schema}\" already exists" msgstr "Lo schema \"{schema}\" per Sqitch esiste giร ." -#: lib/App/Sqitch/Engine/pg.pm:412 +#: lib/App/Sqitch/Engine/pg.pm:452 #, fuzzy msgid "Sqitch registry not initialized" msgstr "Sqitch รจ giร  stato inizializzato" -#: lib/App/Sqitch/Engine/pg.pm:413 +#: lib/App/Sqitch/Engine/pg.pm:453 msgid "" "Because the \"changes\" table does not exist, Sqitch will now initialize the " "database to create its registry tables." @@ -1601,6 +1568,33 @@ msgstr "Lo username non puรฒ contenere \"<\" o iniziare con \"|\"" msgid "User email may not contain \">\"" msgstr "L'email utente non puรฒ contenere \">\"." +#, perl-brace-format +#~ msgid "Loading {file}" +#~ msgstr "Caricamento di {file}" + +#, perl-brace-format +#~ msgid "" +#~ "Deprecated {section} found in {file}; to remove it, run\n" +#~ " {sqitch} config --file {file} --remove-section {section}" +#~ msgstr "" +#~ "Sezione {section} nel file {file} รจ deprecata; per rimuoverla esegui\n" +#~ " {sqitch} config --file {file} --remove-section {section}" + +#~ msgid " - No engines to update" +#~ msgstr " - Nessun motore da aggiornare" + +#, perl-brace-format +#~ msgid "Cannot update {file}. Please make it writable" +#~ msgstr "Impossibile aggiornare il file {file}. Rendilo scrivibile" + +#, perl-brace-format +#~ msgid "" +#~ "Migrated {old} to {new}; To remove {old}, run\n" +#~ " {sqitch} config --file {file} --remove-section {old}" +#~ msgstr "" +#~ "MIgrazioIne da {old} a {new} completata. Per rimuovere {old} esegui\n" +#~ " {sqitch} config --file {file} --remove-section {old}" + #~ msgid "" #~ " The \"{opt}\" option is deprecated;\n" #~ " Instead use \"--dir {dir}={val}\" if available." @@ -1638,13 +1632,3 @@ msgstr "L'email utente non puรฒ contenere \">\"." #~ msgid "The @{old} symbolic tag has been deprecated; use @{new} instead" #~ msgstr "Il tag simbolico @{old} รจ stato deprecato; usa @{new} al suo posto." - -#~ msgid "" -#~ "The core.{engine} config has been deprecated in favor of engine." -#~ "{engine}.\n" -#~ "Run '{sqitch} engine update-config' to update your configurations." -#~ msgstr "" -#~ "La configurazione core.{engine} รจ stata deprecata a favore di engine." -#~ "{engine}.\n" -#~ "Esegui '{sqitch} engine update-config` per aggiornare la tua " -#~ "configurazione." diff --git a/t/add.t b/t/add.t index 5b7e39e43..231df72db 100644 --- a/t/add.t +++ b/t/add.t @@ -3,7 +3,7 @@ use strict; use warnings; use utf8; -use Test::More tests => 236; +use Test::More tests => 238; #use Test::More 'no_plan'; use App::Sqitch; use App::Sqitch::Target; @@ -626,7 +626,14 @@ USAGE: { 'No name arg or option should yield usage'; is_deeply \@args, [$add], 'No args should be passed to usage'; - # Should be true when no engine is specified, either. + # Should get usage when no change name passed or specified. + @args = (); + throws_ok { $add->execute('pg') } qr/USAGE/, + 'No name arg or option should yield usage'; + is_deeply \@args, [$add], 'No args should be passed to usage'; + + # Should get usage when no engine is specified, either. + @args = (); $add = $CLASS->new(sqitch => App::Sqitch->new(config => TestConfig->new)); throws_ok { $add->execute } qr/USAGE/, 'No name arg or option should yield usage'; diff --git a/t/base.t b/t/base.t index 8f3191fb9..9098e2bc6 100644 --- a/t/base.t +++ b/t/base.t @@ -2,12 +2,13 @@ use strict; use warnings; -use Test::More tests => 189; -#use Test::More 'no_plan'; +use Test::More tests => 208; +# use Test::More 'no_plan'; use Test::MockModule 0.17; use Path::Class; use Test::Exception; use Test::NoWarnings; +use Test::Exit; use Capture::Tiny 0.12 qw(:all); use Locale::TextDomain qw(App-Sqitch); use App::Sqitch::X 'hurl'; @@ -133,17 +134,25 @@ GO: { is $sqitch->user_email, 'michelle@whitehouse.gov', 'Should have read user email from environment'; - # Now make it die. - sub puke { App::Sqitch::X->new(@_) } # Ensures we have trace frames. - my $ex = puke(ident => 'ohai', message => 'OMGWTF!'); - $mock->mock(execute => sub { die $ex }); + # Mock outputs. my $sqitch_mock = Test::MockModule->new($CLASS); my @vented; - $sqitch_mock->mock(vent => sub { push @vented => $_[1]; }); + $sqitch_mock->mock(vent => sub { shift; push @vented => @_ }); my $traced; $sqitch_mock->mock(trace => sub { $traced = $_[1]; }); + my @infoed; + $sqitch_mock->mock(info => sub { shift; push @infoed => @_ }); + my @emitted; + $sqitch_mock->mock(emit => sub { shift; push @emitted => @_ }); + + # Now make it die. + sub puke { App::Sqitch::X->new(@_) } # Ensures we have trace frames. + my $ex = puke(ident => 'ohai', message => 'OMGWTF!'); + $mock->mock(execute => sub { die $ex }); is $sqitch->go, 2, 'Go should return 2 on Sqitch exception'; is_deeply \@vented, ['OMGWTF!'], 'The error should have been vented'; + is_deeply \@infoed, [], 'Should have no info output'; + is_deeply \@emitted, [], 'Should have no emitted output'; is $traced, $ex->stack_trace->as_string, 'The stack trace should have been sent to trace'; @@ -154,14 +163,29 @@ GO: { is $sqitch->go, 4, 'Go should return exitval on another exception'; is_deeply \@vented, ['OUCH!', $ex->stack_trace->as_string], 'Both the message and the trace should have been vented'; + is_deeply \@infoed, [], 'Should still have no info output'; + is_deeply \@emitted, [], 'Should still have no emitted output'; + is $traced, undef, 'Nothing should have been traced'; + + # Make it die with a non-fatal exception (error code 1) + @vented = (); + $traced = undef; + $ex = puke( message => 'OOPS!', exitval => 1 ); + is $sqitch->go, 1, 'Go should return exitval on non-fatal exception'; + is_deeply \@vented, [], 'Should not have vented'; + is_deeply \@infoed, ['OOPS!'], 'Should have sent the message to message'; + is_deeply \@emitted, [], 'Should still have no emitted output'; is $traced, undef, 'Nothing should have been traced'; # Make it die without an exception object. $ex = 'LOLZ'; - @vented = (); + @vented = @infoed = (); is $sqitch->go, 2, 'Go should return 2 on a third Sqitch exception'; is @vented, 1, 'Should have one thing vented'; like $vented[0], qr/^LOLZ\b/, 'And it should include our message'; + is_deeply \@infoed, [], 'Should again have no info output'; + is_deeply \@emitted, [], 'Should still have no emitted output'; + is $traced, undef, 'Nothing should have been traced'; } ############################################################################## @@ -212,12 +236,16 @@ PAGER_PROGRAM: { # Ignore warnings while loading IO::Pager. { local $SIG{__WARN__} = sub {}; require IO::Pager } - # Mock the IO::Pager constructor. - my $mock_pager = Test::MockModule->new('IO::Pager'); - $mock_pager->mock(new => sub { return bless => {} => 'IO::Pager' }); - # No pager if no TTY. my $pager_class = -t *STDOUT ? 'IO::Pager' : 'IO::Handle'; + + # Mock the IO::Pager constructor. + my $mock_pager = Test::MockModule->new($pager_class); + $mock_pager->mock(new => sub { return bless => {} => $pager_class }); + my (@said, @printed); + $mock_pager->mock(say => sub { shift; @said = @_ }); + $mock_pager->mock(print => sub { shift; @printed = @_ }); + { local $ENV{SQITCH_PAGER}; local $ENV{PAGER} = "morez"; @@ -225,6 +253,10 @@ PAGER_PROGRAM: { is $sqitch->pager_program, "morez", "pager program should be picked up from PAGER when SQITCH_PAGER and core.pager are not set"; isa_ok $sqitch->pager, $pager_class, 'morez pager'; + lives_ok { $sqitch->page(qw(foo bar)) } 'Should be able to page'; + is_deeply \@said, [qw(foo bar)], 'Should have paged with say()'; + lives_ok { $sqitch->page_literal(qw(foo bar)) } 'Should be able to page literal'; + is_deeply \@printed, [qw(foo bar)], 'Should have paged with print()'; } { @@ -673,3 +705,11 @@ for my $lang (qw(en fr)) { ok utf8::valid($text), 'Localied string should be valid UTF-8'; ok utf8::is_utf8($text), 'Localied string should be decoded'; } + +############################################################################## +# Test interactivity. +lives_ok { $CLASS->_is_interactive } '_is_interactive should not die'; +lives_ok { $CLASS->_is_unattended } '_is_unattended should not die'; + +# Test utilities. +is $CLASS->_bn(__FILE__), 'base.t', '_bn should work'; diff --git a/t/bundle.t b/t/bundle.t index 11c9c00da..11b1cdbc7 100644 --- a/t/bundle.t +++ b/t/bundle.t @@ -3,17 +3,16 @@ use strict; use warnings; use utf8; -use Test::More tests => 306; +use Test::More tests => 301; #use Test::More 'no_plan'; use App::Sqitch; use Path::Class; use Test::Exception; -use Test::Dir; use Test::Warn; use Test::File qw(file_exists_ok file_not_exists_ok); use Test::File::Contents; use Locale::TextDomain qw(App-Sqitch); -use File::Path qw(make_path remove_tree); +use File::Path qw(remove_tree); use Test::NoWarnings; use lib 't/lib'; use MockOutput; @@ -141,42 +140,9 @@ for my $sub (qw(deploy revert verify)) { } ############################################################################## -# Test _mkpath. +# Test _copy(). my $path = dir 'delete.me'; -dir_not_exists_ok $path, "Path $path should not exist"; END { remove_tree $path->stringify if -e $path } -ok $bundle->_mkpath($path), "Create $path"; -dir_exists_ok $path, "Path $path should now exist"; -is_deeply +MockOutput->get_debug, [[' ', __x 'Created {file}', file => $path]], - 'The mkdir info should have been output'; - -# Create it again. -ok $bundle->_mkpath($path), "Create $path again"; -dir_exists_ok $path, "Path $path should still exist"; -is_deeply +MockOutput->get_debug, [], 'Nothing should have been emitted'; - -# Handle errors. -FSERR: { - # Make mkpath to insert an error. - my $mock = Test::MockModule->new('File::Path'); - $mock->mock( mkpath => sub { - my ($file, $p) = @_; - ${ $p->{error} } = [{ $file => 'Permission denied yo'}]; - return; - }); - - throws_ok { $bundle->_mkpath('foo') } 'App::Sqitch::X', - 'Should fail on permission issue'; - is $@->ident, 'bundle', 'Permission error should have ident "bundle"'; - is $@->message, __x( - 'Error creating {path}: {error}', - path => 'foo', - error => 'Permission denied yo', - ), 'The permission error should be formatted properly'; -} - -############################################################################## -# Test _copy(). my $file = file qw(sql deploy roles.sql); my $dest = file $path, qw(deploy roles.sql); file_not_exists_ok $dest, "File $dest should not exist"; @@ -452,13 +418,17 @@ file_not_exists_ok $_ for ($conf_file, @sql, @engine); $config = TestConfig->from(local => 'multiplan.conf'); $sqitch = App::Sqitch->new(config => $config); isa_ok $bundle = $CLASS->new( - sqitch => $sqitch, - config => $config, - all => 1, + sqitch => $sqitch, + config => $config, + all => 1, + from => '@ROOT', dest_dir => dir '_build', ), $CLASS, 'all multiplan bundle command'; ok $bundle->execute, 'Execute multi-target bundle!'; file_exists_ok $_ for ($conf_file, @sql, @engine); +is_deeply +MockOutput->get_warn, [[__( + "Use of --to or --from to bundle multiple targets is not recommended.\nPass them as arguments after each target argument, instead." +)]], 'Should have a warning about --from and -too'; # Make sure we get an error with both --all and a specified target. throws_ok { $bundle->execute('pg' ) } 'App::Sqitch::X', @@ -470,8 +440,8 @@ is $@->message, __( # Try without --all. isa_ok $bundle = $CLASS->new( - sqitch => $sqitch, - config => $sqitch->config, + sqitch => $sqitch, + config => $sqitch->config, dest_dir => dir '_build', ), $CLASS, 'multiplan bundle command'; remove_tree $multidir->stringify; @@ -513,8 +483,8 @@ for my $spec ( # Make sure we handle --to and --from. isa_ok $bundle = $CLASS->new( - sqitch => $sqitch, - config => $sqitch->config, + sqitch => $sqitch, + config => $sqitch->config, from => 'widgets', to => 'widgets', dest_dir => dir '_build', @@ -532,8 +502,8 @@ file_contents_is $engine[0], # Make sure we handle to and from args. isa_ok $bundle = $CLASS->new( - sqitch => $sqitch, - config => $sqitch->config, + sqitch => $sqitch, + config => $sqitch->config, dest_dir => dir '_build', ), $CLASS, 'another bundle command'; remove_tree $multidir->stringify; @@ -570,3 +540,15 @@ is $@->message, __nx( 3, arg => join ', ', qw(ba da dum) ), 'Unknown arguments error message should be correct'; + +# Should die on both changes and --from or -to. +isa_ok $bundle = $CLASS->new( + sqitch => $sqitch, + config => $sqitch->config, + from => '@ROOT', +), $CLASS, 'all multiplan bundle command'; +throws_ok { $bundle->execute(qw(widgets)) } 'App::Sqitch::X', + 'Should get an exception a change name and --from'; +is $@->ident, 'bundle', 'Conflicting arguments error ident shoud be "bundle"'; +is $@->message, __('Cannot specify both --from or --to and change arguments'), + 'Conflicting arguments error message should be correct'; diff --git a/t/cockroach.t b/t/cockroach.t new file mode 100644 index 000000000..3b070a5cf --- /dev/null +++ b/t/cockroach.t @@ -0,0 +1,180 @@ +#!/usr/bin/perl -w + +# To test against a live Cockroach database, you must set the +# SQITCH_TEST_COCKROACH_URI environment variable. This is a standard URI::db +# URI, and should look something like this: +# +# export SQITCH_TEST_COCKROACH_URI=db:cockroach://root:password@localhost:26257/sqitchtest +# + +use strict; +use warnings; +use 5.010; +use Test::More 0.94; +use Test::MockModule; +use Locale::TextDomain qw(App-Sqitch); +use Try::Tiny; +use App::Sqitch; +use App::Sqitch::Target; +use App::Sqitch::Plan; +use Path::Class; +use lib 't/lib'; +use DBIEngineTest; +use TestConfig; + +my $CLASS; + +BEGIN { + $CLASS = 'App::Sqitch::Engine::cockroach'; + require_ok $CLASS or die; + delete $ENV{PGPASSWORD}; +} + +my $uri = URI::db->new('db:cockroach:'); +my $config = TestConfig->new('core.engine' => 'cockroach'); +my $sqitch = App::Sqitch->new(config => $config); +my $target = App::Sqitch::Target->new( + sqitch => $sqitch, + uri => $uri, +); +isa_ok my $cockroach = $CLASS->new(sqitch => $sqitch, target => $target), $CLASS; + +is $cockroach->key, 'cockroach', 'Key should be "cockroach"'; +is $cockroach->name, 'CockroachDB', 'Name should be "CockroachDB"'; +is $cockroach->driver, 'DBD::Pg 2.0', 'Driver should be "DBD::Pg 2.0"'; +is $cockroach->wait_lock, 1, 'wait_lock should return 1'; + +############################################################################## +# Test DateTime formatting stuff. +ok my $ts2char = $CLASS->can('_ts2char_format'), "$CLASS->can('_ts2char_format')"; +is sprintf($ts2char->($cockroach), 'foo'), + q{experimental_strftime(foo AT TIME ZONE 'UTC', 'year:%Y:month:%m:day:%d:hour:%H:minute:%M:second:%S:time_zone:UTC')}, + '_ts2char_format should work'; + +ok my $dtfunc = $CLASS->can('_dt'), "$CLASS->can('_dt')"; +isa_ok my $dt = $dtfunc->( + 'year:2012:month:07:day:05:hour:15:minute:07:second:01:time_zone:UTC' +), 'App::Sqitch::DateTime', 'Return value of _dt()'; +is $dt->year, 2012, 'DateTime year should be set'; +is $dt->month, 7, 'DateTime month should be set'; +is $dt->day, 5, 'DateTime day should be set'; +is $dt->hour, 15, 'DateTime hour should be set'; +is $dt->minute, 7, 'DateTime minute should be set'; +is $dt->second, 1, 'DateTime second should be set'; +is $dt->time_zone->name, 'UTC', 'DateTime TZ should be set'; + +############################################################################## +# Test table error methods. +DBI: { + local *DBI::state; + ok !$cockroach->_no_table_error, 'Should have no table error'; + ok !$cockroach->_no_column_error, 'Should have no column error'; + + $DBI::state = '42703'; + ok !$cockroach->_no_table_error, 'Should again have no table error'; + ok $cockroach->_no_column_error, 'Should now have no column error'; + + $DBI::state = '42P01'; + ok $cockroach->_no_table_error, 'Should now have table error'; + ok !$cockroach->_no_column_error, 'Still should have no column error'; +} + +############################################################################## +# Test _run_registry_file. +RUNREG: { + # Mock I/O used by _run_registry_file. + my $mock_engine = Test::MockModule->new($CLASS); + my @ran; + $mock_engine->mock(_run => sub { shift; push @ran, \@_ }); + + # Mock up the database handle. + my $dbh = DBI->connect('dbi:Mem:', undef, undef, {}); + $mock_engine->mock(dbh => $dbh ); + my $mock_dbd = Test::MockModule->new(ref $dbh, no_auto => 1); + my @done; + $mock_dbd->mock(do => sub { shift; push @done, \@_; 1 }); + + # Find the SQL file. + my $ddl = file($INC{'App/Sqitch/Engine/cockroach.pm'})->dir->file('cockroach.sql'); + + # Test it! + my $registry = $cockroach->registry; + ok $cockroach->_run_registry_file($ddl), 'Run the registry file'; + is_deeply \@ran, [[ + '--file' => $ddl, + '--set' => "registry=$registry", + ]], 'Shoud have deployed the original SQL file'; + is_deeply \@done, [['SET search_path = ?', undef, $registry]], + 'The registry should have been added to the search path'; +} + +############################################################################## +# Can we do live tests? +$config->replace('core.engine' => 'cockroach'); +$sqitch = App::Sqitch->new(config => $config); +$target = App::Sqitch::Target->new( sqitch => $sqitch ); +$cockroach = $CLASS->new(sqitch => $sqitch, target => $target); + +$uri = URI->new( + $ENV{SQITCH_TEST_COCKROACH_URI} + || 'db:cockroach://' . ($ENV{PGUSER} || 'root') . "\@localhost/" +); + +my $dbh; +my $id = DBIEngineTest->randstr; +my ($db, $reg1, $reg2) = map { $_ . $id } qw(__sqitchtest__ sqitch __sqitchtest); + +END { + return unless $dbh; + $dbh->{Driver}->visit_child_handles(sub { + my $h = shift; + $h->disconnect if $h->{Type} eq 'db' && $h->{Active} && $h ne $dbh; + }); + + # Drop the database or schema. + $dbh->do("DROP DATABASE $db") if $dbh->{Active} +} + +my $err = try { + $cockroach->_capture('--version'); + $cockroach->use_driver; + $dbh = DBI->connect($uri->dbi_dsn, $uri->user, $uri->password, { + PrintError => 0, + RaiseError => 1, + AutoCommit => 1, + cockroach_lc_messages => 'C', + }); + $dbh->do("CREATE DATABASE $db"); + $uri->dbname($db); + undef; +} catch { + eval { $_->message } || $_; +}; + +DBIEngineTest->run( + class => $CLASS, + version_query => 'SELECT version()', + target_params => [ uri => $uri, registry => $reg1 ], + alt_target_params => [ uri => $uri, registry => $reg2 ], + skip_unless => sub { + my $self = shift; + die $err if $err; + # Make sure we have psql and can connect to the database. + my $version = $self->sqitch->capture( $self->client, '--version' ); + say "# Detected $version"; + $self->_capture('--command' => 'SELECT version()'); + }, + engine_err_regex => qr/^ERROR: /, + init_error => __x( + 'Sqitch schema "{schema}" already exists', + schema => $reg2, + ), + test_dbh => sub { + my $dbh = shift; + # Make sure the sqitch schema is the first in the search path. + is $dbh->selectcol_arrayref('SELECT current_schema')->[0], + $reg2, 'The Sqitch schema should be the current schema'; + }, +); + +done_testing; diff --git a/t/command.t b/t/command.t index 1b2c85ba4..140da2b92 100644 --- a/t/command.t +++ b/t/command.t @@ -4,7 +4,7 @@ use strict; use warnings; use 5.010; use utf8; -use Test::More tests => 182; +use Test::More tests => 198; #use Test::More 'no_plan'; use Test::NoWarnings; use List::Util qw(first); @@ -26,6 +26,8 @@ use App::Sqitch::Target; use Test::Exception; use Test::NoWarnings; use Test::MockModule; +use Test::Dir; +use File::Path qw(make_path remove_tree); use Locale::TextDomain qw(App-Sqitch); use Capture::Tiny 0.12 ':all'; use Path::Class; @@ -405,6 +407,10 @@ ARGS: { 'Single unknown arg raise an error'; is $@->ident, 'whu', 'Unknown error ident should be "whu"'; is $@->message, $msg->('foo'), 'Unknown error message should be correct'; + throws_ok { $parsem->( args => ['Changes'] ) } 'App::Sqitch::X', + 'Single invavlid fiile arg raise an error'; + is $@->ident, 'whu', 'Unknown error ident should be "whu"'; + is $@->message, $msg->('Changes'), 'Unknown file error message should be correct'; is_deeply $parsem->( args => ['hey'] ), [['devdb'], ['hey']], 'Single change should be recognized as change'; is_deeply $parsem->( args => ['devdb'] ), [['devdb'], []], @@ -723,3 +729,46 @@ like capture_stderr { throws_ok { $cmd->usage('Invalid whozit') } qr/EXITED: 2/ }, qr/\Qsqitch [options] [command-options] [args]/, 'usage should prefer sqitch-$command-usage'; + +############################################################################## +# Test _mkpath. +require MockOutput; +my $path = dir 'delete.me'; +dir_not_exists_ok $path, "Path $path should not exist"; +END { remove_tree $path->stringify if -e $path } +ok $cmd->_mkpath($path), "Create $path"; +dir_exists_ok $path, "Path $path should now exist"; +is_deeply +MockOutput->get_debug, [[' ', __x 'Created {file}', file => $path]], + 'The mkdir info should have been output'; + +# Create it again. +ok $cmd->_mkpath($path), "Create $path again"; +dir_exists_ok $path, "Path $path should still exist"; +is_deeply +MockOutput->get_debug, [], 'Nothing should have been emitted'; + +# Handle errors. +FSERR: { + # Make mkpath to insert an error. + my $mock = Test::MockModule->new('File::Path'); + $mock->mock( mkpath => sub { + my ($file, $p) = @_; + ${ $p->{error} } = [{ $file => 'Permission denied yo'}]; + return; + }); + + throws_ok { $cmd->_mkpath('foo') } 'App::Sqitch::X', + 'Should fail on permission issue'; + is $@->ident, 'good', 'Permission error should have ident "good"'; + is $@->message, __x( + 'Error creating {path}: {error}', + path => 'foo', + error => 'Permission denied yo', + ), 'The permission error should be formatted properly'; + + # Try an error with no path. + throws_ok { $cmd->_mkpath('') } 'App::Sqitch::X', + 'Should fail on nonexistent file'; + is $@->ident, 'good', 'Nonexistant path error should have ident "good"'; + is $@->message, 'Permission denied yo', + 'Nonexistant path error should be the message'; +} diff --git a/t/config.t b/t/config.t index 8c45166ee..96237637f 100644 --- a/t/config.t +++ b/t/config.t @@ -2,8 +2,8 @@ use strict; use warnings; -use Test::More tests => 346; -#use Test::More 'no_plan'; +use Test::More tests => 360; +# use Test::More 'no_plan'; use File::Spec; use Test::MockModule; use Test::Exception; @@ -477,6 +477,7 @@ engine.sqlite.client=/opt/local/bin/sqlite3 engine.sqlite.registry=meta engine.sqlite.target=devdb foo.BAR.baz=hello +foo.BAR.yep guess.Yes.No.calico=false guess.Yes.No.red=true revert.count=2 @@ -511,6 +512,7 @@ core.top_dir=migrations core.uri=https://github.com/sqitchers/sqitch/ engine.pg.client=/usr/local/pgsql/bin/psql foo.BAR.baz=hello +foo.BAR.yep guess.Yes.No.calico=false guess.Yes.No.red=true revert.count=2 @@ -835,8 +837,15 @@ is_deeply \@emit, [[q{engine.pg.user=theory} ]], 'Should match all engine.pg options that match'; @emit = (); +ok $cmd->execute('foo\\.BAR\\..+', ''), + 'Call get_regex on foo\\Bar\\..+ and always-matching regex'; +is_deeply \@emit, [[q{foo.BAR.baz=hello +foo.BAR.yep} +]], 'Should include key with no value'; +@emit = (); + throws_ok { $cmd->execute('engine\\.pg\\..+', 'x$') } 'App::Sqitch::X', - 'Attempt to get_regex core.foo with non-matching regex should fail'; + 'Attempt to get_regex engine\\.pg with non-matching regex should fail'; is $@->ident, 'config', 'Error ident should be "config"'; is $@->message, '', 'Error Message should be empty'; is $@->exitval, 1, 'Error exitval should be 1'; @@ -1091,9 +1100,39 @@ $cmd->add('core.foo', 'hi'); $cmd->add('core.foo', 'bye'); throws_ok { $cmd->set('core.foo', 'hi') } 'App::Sqitch::X', 'Should fail setting multi-value key'; -is $@->ident, 'config', 'Mult-valkue key exception ident should be "config"'; +is $@->ident, 'config', 'Multi-value key exception ident should be "config"'; is $@->message, __('Cannot overwrite multiple values with a single value'), 'The multi-value key error should be thrown'; +SETERR: { + my $mock_cfg = TestConfig->mock( + set => sub { die 'XXX' }, + rename_section => sub { die 'YYY' }, + remove_section => sub { die 'ZZZ' }, + ); + throws_ok { $cmd->set('core.xxx', 'hi') } 'App::Sqitch::X', + 'Set should fail on App::Sqitch::Cofig error'; + is $@->ident, 'config', 'Set exception ident should be "config"'; + like $@->message, qr/^XXX/, + 'Config set exception message should propagate'; + + throws_ok { $cmd->unset('core.foo') } 'App::Sqitch::X', + 'Unset should fail on App::Sqitch::Cofig error'; + is $@->ident, 'config', 'Unset exception ident should be "config"'; + like $@->message, qr/^XXX/, + 'Config set exception message should propagate'; + + throws_ok { $cmd->rename_section('core.foo', 'core.bar') } 'App::Sqitch::X', + 'Rename should fail on App::Sqitch::Cofig error'; + is $@->ident, 'config', 'Rename exception ident should be "config"'; + like $@->message, qr/^YYY/, + 'Config rename exception message should propagate'; + + throws_ok { $cmd->remove_section('core') } 'App::Sqitch::X', + 'Remove should fail on App::Sqitch::Cofig error'; + is $@->ident, 'config', 'Remove exception ident should be "config"'; + like $@->message, qr/^ZZZ/, + 'Config remove exception message should propagate'; +} ############################################################################## # Test edit(). diff --git a/t/configuration.t b/t/configuration.t index 9dca488dd..7c2662e3d 100644 --- a/t/configuration.t +++ b/t/configuration.t @@ -80,7 +80,8 @@ is_deeply $config->get_section(section => 'engine.pg'), { # Make sure it works with irregular casing. is_deeply $config->get_section(section => 'foo.BAR'), { - baz => 'hello' + baz => 'hello', + yep => undef, }, 'get_section() whould work with capitalized subsection'; # Should work with multiple subsections and case-preserved keys. diff --git a/t/dbiengine_role.t b/t/dbiengine_role.t new file mode 100644 index 000000000..ecd793ec6 --- /dev/null +++ b/t/dbiengine_role.t @@ -0,0 +1,130 @@ +#!/usr/bin/perl -w + +use strict; +use warnings; +use 5.010; +use utf8; +use Test::More tests => 14; +# use Test::More 'no_plan'; +use Test::MockModule; +use Test::Exception; + +# For testing paths in App::Sqitch::Role::DBIEngine that are not implicictly +# tested by the engines that use it. + +my $CLASS; + +BEGIN { + $CLASS = 'App::Sqitch::Role::DBIEngine'; + require_ok $CLASS or die; +} + +can_ok $CLASS, qw( + _dt + _log_tags_param + _log_requires_param + _log_conflicts_param + _ts_default + _can_limit + _limit_default + _simple_from + _quote_idents + _in_expr + _register_release + _version_query + registry_version + _cid + earliest_change_id + latest_change_id + _select_state + current_state + current_changes + current_tags + search_events + _regex_expr + _limit_offset + registered_projects + register_project + is_deployed_change + are_deployed_changes + is_deployed_tag + _multi_values + _dependency_placeholders + _tag_placeholders + _tag_subselect_columns + _prepare_to_log + log_deploy_change + log_fail_change + _log_event + changes_requiring_change + name_for_change_id + log_new_tags + log_revert_change + deployed_changes + deployed_changes_since + load_change + _offset_op + change_id_offset_from_id + change_offset_from_id + _cid_head + change_id_for + _update_script_hashes + begin_work + finish_work + rollback_work +); + +is App::Sqitch::Role::DBIEngine::_ts_default, 'DEFAULT', + '_ts_default shoudld return DEFAULT'; + +# Test various failure modes. +my $role = bless {} => $CLASS; +FAILMOCKS: { + # Set up mocks. + my $mock = Test::MockModule->new($CLASS); + my ($dbh_err, $no_table, $no_col, $init) = ('OW', 0, 0, 0); + my ($state_err, $sel_state) = ('OOPS', -1); + $mock->mock(dbh => sub { die $dbh_err }); + $mock->mock(_no_table_error => sub { $no_table }); + $mock->mock(initialized => sub { $init }); + $mock->mock(_no_column_error => sub { $no_col }); + $mock->mock(_select_state => sub { $sel_state = $_[2]; die $state_err }); + + # Test registry_version. + throws_ok { $role->registry_version } qr/OW/, + 'registry_version should propagate non-table error'; + $no_table = 1; + ok !$role->registry_version, + 'registry_version should return false on no-table error'; + $no_table = 0; + + # Test _cid + throws_ok { $role->_cid(0, 0, 'foo') } qr/OW/, + '_cid should propagate non-table error'; + $no_table = 1; + ok !$role->_cid(0, 0, 'foo'), + '_cid should return false on no-table error and unititialized'; + $no_table = 0; + + + # Test current_state. + throws_ok { $role->current_state('foo') } qr/OOPS/, + 'curent_state should propagate _select_state error'; + is $sel_state, 1, 'Should have passed 1 to _select_state'; + ($sel_state, $no_table) = (-1, 1); + ok !$role->current_state('foo'), + 'curent_state should return false on no-table error'; + is $sel_state, 1, 'Should again have passed 1 to _select_state'; + ($sel_state, $init, $no_col) = (-1, 1, 1); + throws_ok { $role->current_state('foo') } qr/OOPS/, + 'curent_state should propagate second error on no-column error'; + is $sel_state, 0, 'Should again have passed 0 to _select_state'; + ($sel_state, $no_table, $init, $no_col) = (-1, 0, 0, 0); + + # Make sure change_id_for returns undef when no useful params. + $mock->mock(dbh => 1); + is $role->change_id_for(project => 'foo'), undef, + 'Should get undef from change_id_for when no useful params'; +} + +done_testing; diff --git a/t/engine.t b/t/engine.t index 2f91e9e8f..f923ab721 100644 --- a/t/engine.t +++ b/t/engine.t @@ -4,7 +4,7 @@ use strict; use warnings; use 5.010; use utf8; -use Test::More tests => 705; +use Test::More tests => 769; # use Test::More 'no_plan'; use App::Sqitch; use App::Sqitch::Plan; @@ -94,6 +94,7 @@ ENGINE: { sub finish_work { push @SEEN => ['finish_work'] if $record_work } sub log_new_tags { push @SEEN => [ log_new_tags => $_[1] ]; $_[0] } sub _update_script_hashes { push @SEEN => ['_update_script_hashes']; $_[0] } + sub upgrade_registry { push @SEEN => 'upgrade_registry' } sub seen { [@SEEN] } after seen => sub { @SEEN = () }; @@ -349,6 +350,9 @@ for my $abs (qw( registered_projects change_offset_from_id change_id_offset_from_id + wait_lock + registry_version + _update_script_hashes )) { throws_ok { $engine->$abs } qr/\Q$CLASS has not implemented $abs()/, "Should get an unimplemented exception from $abs()" @@ -360,12 +364,11 @@ can_ok $engine, '_load_changes'; my $now = App::Sqitch::DateTime->now; my $plan = $target->plan; -# Mock App::Sqitch::DateTime so that dbchange tags all have the same +# Mock App::Sqitch::DateTime so that change tags all have the same # timestamps. my $mock_dt = Test::MockModule->new('App::Sqitch::DateTime'); $mock_dt->mock(now => $now); - for my $spec ( [ 'no change' => [] ], [ 'undef' => [undef] ], @@ -829,7 +832,27 @@ is_deeply +MockOutput->get_info_literal, [[ ]], 'Output should reflect logged reversion'; is_deeply +MockOutput->get_info, [[__ 'ok']], 'Output should acknowldge revert success'; + +# Have the log throw an error. +$die = 'log_revert_change'; +throws_ok { $engine->revert_change($change) } + 'App::Sqitch::X', 'Should die on unknown revert logging error'; +is $@->ident, 'revert', 'Sould have revert ident error'; +is $@->message, 'Revert failed','Should get revert failure error message'; +is_deeply $engine->seen, [ + ['begin_work'], + ['finish_work'], +], 'Log failure should not have seen log_rever_change'; +is_deeply +MockOutput->get_info_literal, [[ + ' - foo ..', '..', ' ' +]], 'Output should reflect reversion'; +is_deeply +MockOutput->get_info, [[__ 'not ok']], + 'Output should acknowldge success failure'; +is_deeply +MockOutput->get_vent, [ + ['AAAH!'], +], 'The logging error should have been vented'; $record_work = 0; +$die = ''; ############################################################################## # Test earliest_change() and latest_change(). @@ -933,6 +956,22 @@ is_deeply $engine->seen, [ ['log_new_tags' => $plan->change_at(1)], ], 'Should have updated hashes but not IDs'; +# Have current_state return no script hash. +my $mock_whu = Test::MockModule->new('App::Sqitch::Engine::whu'); +my $state = {change_id => $latest_change_id}; +$mock_whu->mock(current_state => $state); +ok $engine->_sync_plan, 'Sync the plan with no script hash'; +$mock_whu->unmock('current_state'); +is $plan->position, 1, 'Plan should now be at position 1'; +is $engine->start_at, 'users@alpha', 'start_at should still be users@alpha'; +is_deeply $engine->seen, [ + 'upgrade_registry', + ['_update_script_hashes'], + ['log_new_tags' => $plan->change_at(1)], +], 'Should have ugpraded the registry'; +is $state->{script_hash}, $latest_change_id, + 'The script hash should have been set to the change ID'; + ############################################################################## # Test deploy. can_ok $CLASS, 'deploy'; @@ -1042,6 +1081,7 @@ is_deeply $engine->seen, [ [lock_destination => []], [current_state => undef], 'initialized', + 'upgrade_registry', 'register_project', [check_deploy_dependencies => [$plan, 1]], [run_file => $changes[0]->deploy_file], @@ -1091,7 +1131,7 @@ is_deeply +MockOutput->get_info, [ # Start with widgets. $latest_change_id = $changes[2]->id; throws_ok { $engine->deploy('@alpha') } 'App::Sqitch::X', - 'Should fail changeing older change'; + 'Should fail deploying older change'; is $@->ident, 'deploy', 'Should be a "deploy" error'; is $@->message, __ 'Cannot deploy to an earlier change; use "revert" instead', 'It should suggest using "revert"'; @@ -1101,6 +1141,28 @@ is_deeply $engine->seen, [ ['log_new_tags' => $changes[2]], ], 'Should have called latest_item() and latest_tag()'; +# Make sure that it upgrades the registry when deploying on existing changes. +$latest_change_id = undef; +my $mock_plan = Test::MockModule->new(ref $plan); +my $orig_pos_meth; +my @pos_vals = (1, 1); +$mock_plan->mock(position => sub { return @pos_vals ? shift @pos_vals : $orig_pos_meth->($_[0]) }); +$orig_pos_meth = $mock_plan->original('position'); +ok $engine->deploy(), 'Deploy to from index 1'; +$mock_plan->unmock('position'); +is $plan->position, 2, 'Plan should be at position 2'; +is_deeply $engine->seen, [ + [lock_destination => []], + [current_state => undef], + 'upgrade_registry', + [check_deploy_dependencies => [$plan, 2]], +], 'Should have deployed to change 2'; +is_deeply +MockOutput->get_info, [ + [__x 'Deploying changes to {destination}', destination => $engine->destination ], + # [__ 'ok'], + # [__ 'ok'], +], 'Should have emitted deploy announcement and successes'; + # Make sure we can deploy everything by change. $latest_change_id = undef; $plan->reset; @@ -1112,6 +1174,7 @@ is_deeply $engine->seen, [ [lock_destination => []], [current_state => undef], 'initialized', + 'upgrade_registry', 'register_project', [check_deploy_dependencies => [$plan, 3]], [run_file => $changes[0]->deploy_file], @@ -1163,6 +1226,7 @@ is_deeply $engine->seen, [ [lock_destination => []], [current_state => undef], 'initialized', + 'upgrade_registry', 'register_project', [check_deploy_dependencies => [$plan, 3]], ], 'It should have check for initialization'; @@ -1288,7 +1352,6 @@ $plan->add(name => 'curry' ); # Make it die. $plan->position(1); -my $mock_whu = Test::MockModule->new('App::Sqitch::Engine::whu'); $mock_whu->mock(log_deploy_change => sub { hurl 'ROFL' if $_[1] eq $changes[-1] }); throws_ok { $engine->_deploy_by_tag($plan, $#changes) } 'App::Sqitch::X', 'Die in log_deploy_change'; @@ -1657,6 +1720,47 @@ is_deeply +MockOutput->get_vent, [ ['ROFL'], [__x 'Reverting to {change}', change => '@alpha'], ], 'Should notifiy user of error and rollback to @alpha'; + +# Die with a string rather than an exception. +$plan->position(2); +$engine->start_at('@alpha'); +$mock_whu->mock(run_file => sub { die 'Oops' if $_[1]->basename eq 'dr_evil.sql' }); +throws_ok { $engine->_deploy_all($plan, $plan->count -1 ) } 'App::Sqitch::X', + 'Die in _deploy_all on the last change'; +is $@->message, __('Deploy failed'), 'Should once again get final deploy failure message'; +is_deeply $engine->seen, [ + [log_deploy_change => $changes[3]], + [log_deploy_change => $changes[4]], + [log_deploy_change => $changes[5]], + [log_fail_change => $changes[6]], + [log_revert_change => $changes[5]], + [log_revert_change => $changes[4]], + [log_revert_change => $changes[3]], +], 'Should have deployed to dr_evil and revered down to @alpha'; + +is_deeply +MockOutput->get_info_literal, [ + [' + lolz ..', '.........', ' '], + [' + tacos ..', '........', ' '], + [' + curry ..', '........', ' '], + [' + dr_evil ..', '......', ' '], + [' - curry ..', '........', ' '], + [' - tacos ..', '........', ' '], + [' - lolz ..', '.........', ' '], +], 'Should see changes revert back to @alpha'; +is_deeply +MockOutput->get_info, [ + [__ 'ok' ], + [__ 'ok' ], + [__ 'ok' ], + [__ 'not ok' ], + [__ 'ok' ], + [__ 'ok' ], + [__ 'ok' ], +], 'Output should reflect deploy successes and failures'; +$vented = MockOutput->get_vent; +is @{ $vented }, 2, 'Should have two vented items'; +like $vented->[0][0], qr/Oops/, 'First vented should be the error'; +is_deeply $vented->[1], [__x 'Reverting to {change}', change => '@alpha'], + 'Should notifiy user of rollback to @alpha'; $mock_whu->unmock_all; ############################################################################## @@ -1692,6 +1796,52 @@ is_deeply +MockOutput->get_info, [ [__ 'ok' ], ], 'Output should reflect deploy success'; +# Make the logging die. +$mock_whu->mock(log_deploy_change => sub { hurl test => 'OHNO' }); +throws_ok { $engine->deploy_change($change) } 'App::Sqitch::X', + 'Deploying change should die on logging failure'; +is $@->ident, 'private', 'Should have privat ident'; +is $@->message, __('Deploy failed'), 'Should have failure message'; +is_deeply $engine->seen, [ + [run_file => $change->deploy_file], + [run_file => $change->revert_file], + ['log_fail_change', $change], +], 'It should have been deployed and reverted'; +is_deeply +MockOutput->get_info_literal, [ + [' + foo ..', '..........', ' '] +], 'Should have shown change name'; +is_deeply +MockOutput->get_info, [ + [__ 'not ok' ], +], 'Output should reflect deploy failure'; +is_deeply +MockOutput->get_vent, [ + ['OHNO'] +], 'Vent should reflect deployment error'; + +# Also make the revert fail. +$mock_whu->mock('run_revert' => sub { hurl test => 'NO REVERT' }); +throws_ok { $engine->deploy_change($change) } 'App::Sqitch::X', + 'Deploying change should die on logging failure'; +is $@->ident, 'private', 'Should have privat ident'; +is $@->message, __('Deploy failed'), 'Should have failure message'; +is_deeply $engine->seen, [ + [run_file => $change->deploy_file], + ['log_fail_change', $change], +], 'It should have been deployed but not reverted'; +is_deeply +MockOutput->get_info_literal, [ + [' + foo ..', '..........', ' '] +], 'Should have shown change name'; +is_deeply +MockOutput->get_info, [ + [__ 'not ok' ], +], 'Output should reflect deploy failure'; +is_deeply +MockOutput->get_vent, [ + ['OHNO'], + ['NO REVERT'], +], 'Vent should reflect deployment and reversion errors'; + +# Unmock. +$mock_whu->unmock('log_deploy_change'); +$mock_whu->unmock('run_revert'); + my $make_deps = sub { my $conflicts = shift; return map { @@ -1883,7 +2033,7 @@ my @dbchanges; } @changes[0..3]; MockOutput->ask_yes_no_returns(1); -ok $engine->revert, 'Revert all changes'; +is $engine->revert, $engine, 'Revert all changes'; is_deeply $engine->seen, [ [lock_destination => []], [deployed_changes => undef], @@ -2220,6 +2370,20 @@ is_deeply $engine->seen, [ ], 'The change file should have been run'; is_deeply +MockOutput->get_info, [], 'Should have no info output'; +# Should raise an error when the verfiy fails script fails. +$mock_engine->mock(run_verify => sub { die 'OHNO' }); +throws_ok { $engine->verify_change($change) } 'App::Sqitch::X', + 'Should throw error on verify failure'; +$mock_engine->unmock('run_verify'); +is $@->ident, 'verify', 'Verify error ident should be "verify"'; +like $@->previous_exception, qr/OHNO/, 'Previous exception should be captured'; +is $@->message, __x( + 'Verify script "{script}" failed.', + script => $change->verify_file +), 'Verify error message should be correct'; +is_deeply $engine->seen, [], 'Should have seen not method calls'; +is_deeply +MockOutput->get_info, [], 'Should have no info output'; + # Try a change with no verify script. $change = App::Sqitch::Plan::Change->new( name => 'roles', plan => $target->plan ); ok $engine->verify_change($change), 'Verify a change with no verify script.'; @@ -2243,6 +2407,23 @@ CHECK_DEPLOY_DEPEND: { [ are_deployed_changes => [map { $plan->change_at($_) } 0..$plan->count - 1] ], ], 'Should have called are_deployed_changes'; + # Fail when some changes are already deployed. + my @deployed = map { $plan->change_at($_) } 0, 2; + @deployed_change_ids = map { $_->id } @deployed; + throws_ok { $engine->check_deploy_dependencies($plan) } 'App::Sqitch::X', + 'Should die when some changes deployed'; + is $@->ident, 'deploy', 'Already deployed error ident should be "deploy"'; + is $@->message, __nx( + 'Change "{changes}" has already been deployed', + 'Changes have already been deployed: {changes}', + scalar @deployed_change_ids, + changes => join(', ', map { $_->format_name_with_tags . " (" . $_->id . ")" } @deployed), + ); + is_deeply $engine->seen, [ + [ are_deployed_changes => [map { $plan->change_at($_) } 0..$plan->count - 1] ], + ], 'Should have called are_deployed_changes'; + @deployed_change_ids = (); + # Make sure it works when depending on a previous change. my $change = $plan->change_at(3); push @{ $change->_requires } => $make_deps->( 0, 'users' ); @@ -2490,6 +2671,14 @@ CHECK_REVERT_DEPEND: { plan => $plan, ); + # First test with no dependencies. + @requiring = []; + ok $engine->check_revert_dependencies($change), + 'Should get no error with no dependencies'; + is_deeply $engine->seen, [ + [changes_requiring_change => $change ], + ], 'It should have check for requiring changes'; + # Have revert change fail with requiring changes. my $req = { change_id => '23234234', @@ -2893,7 +3082,6 @@ is_deeply +MockOutput->get_comment, [[__ 'Not present in the plan' ]], is_deeply $engine->seen, [], 'No verify script should have been run'; # Try a change in the wrong place in the plan. -my $mock_plan = Test::MockModule->new(ref $plan); $mock_plan->mock(index_of => 5); is $engine->_verify_changes(1, 0, 0, $changes[1]), 1, 'Verify of an out-of-order change should return errcount 1'; @@ -3394,6 +3582,43 @@ is_deeply +MockOutput->get_info, [[__x( )]], 'Should have notified user of waiting for lock'; is_deeply $engine->seen, ['wait_lock'], 'wait_lock should have been called'; +############################################################################## +# Test _to_idx() +$mock_whu->mock(latest_change_id => 2); +is $engine->_to_idx, $plan->count-1, + 'Should get last index when there is a latest change ID'; +$mock_whu->unmock('latest_change_id'); + +############################################################################## +# Test _handle_lookup_index() with change names not in the plan. +throws_ok { $engine->_handle_lookup_index('foo', [qw(x y)]) } 'App::Sqitch::X', + 'Should die on too many IDs'; +is $@->ident, 'engine', 'Too many IDs ident should be "engine"'; +is $@->message, __('Change Lookup Failed'), + 'Too many IDs message should be correct'; +is_deeply +MockOutput->get_vent, [ + [__x( + 'Change "{change}" is ambiguous. Please specify a tag-qualified change:', + change => 'foo', + )], + [ ' * ', 'bugaboo' ], + [ ' * ', 'bugaboo' ], +], 'Too many IDs error should have been vented'; + +############################################################################## +# Test planned_deployed_common_ancestor_id. +is $engine->planned_deployed_common_ancestor_id, + '0539182819c1f0cb50dc4558f4f80b1a538a01b2', + 'Test planned_deployed_common_ancestor_id'; + +############################################################################## +# Test default implementations. +is $engine->key, 'whu', 'Should have key'; +is $engine->driver, $engine->key, 'Driver should be the same as engine'; +ok $CLASS->try_lock, 'Default try_lock should return true by default'; +is $CLASS->begin_work, $CLASS, 'Default begin_work should return self'; +is $CLASS->finish_work, $CLASS, 'Default finish_work should return self'; + __END__ diag $_->format_name_with_tags for @changes; diag '======'; diff --git a/t/engine_cmd.t b/t/engine_cmd.t index c6b69bf62..70a9a8a05 100644 --- a/t/engine_cmd.t +++ b/t/engine_cmd.t @@ -4,7 +4,7 @@ use strict; use warnings; use utf8; use Test::More tests => 201; -#use Test::More 'no_plan'; +# use Test::More 'no_plan'; use App::Sqitch; use Locale::TextDomain qw(App-Sqitch); use Test::Exception; @@ -54,7 +54,6 @@ can_ok $cmd, qw( remove rm show - update_config does ); @@ -179,6 +178,35 @@ is_deeply +MockOutput->get_emit, [ ["sqlite\twidgets"] ], 'The list of engines and their targets should have been output'; +############################################################################## +# Test _target(). +TARGET: { + isa_ok $cmd = $CLASS->new({ + sqitch => App::Sqitch->new( config => $config, options => { }) + }), $CLASS, 'New engine'; + + is $cmd->_target('pg', undef), undef, 'Target should be undef'; + is $cmd->_target('pg', 'db:pg:'), 'db:pg:', + 'Target should fall back on passed name'; + + throws_ok { $cmd->_target('pg', 'db:sqlite:') } 'App::Sqitch::X', + 'Should get error for mismatched target engine'; + is $@->ident, 'engine', 'Mismatched target error ident should be "engine"'; + is $@->message, __x( + 'Cannot assign URI using engine "{new}" to engine "{old}"', + new => 'sqlite', + old => 'pg', + ), 'Mismatched target error message should be correct'; + + throws_ok { $cmd->_target('pg', 'nonesuch') } 'App::Sqitch::X', + 'Should get error for unknown target'; + is $@->ident, 'engine', 'Uknown target error ident should be "engine"'; + is $@->message, __x( + 'Unknown target "{target}"', + target => 'nonesuch' + ), 'Unkonwn target error message should be correct'; +} + ############################################################################## # Test add(). MISSINGARGS: { @@ -580,80 +608,3 @@ MISSINGARGS: { action => 'nonexistent', )], 'Nonexistent action message should be passed to usage'; } - -############################################################################## -# Test update_config. -$config->group_set($config->local_file, [ - {key => 'core.mysql.target', value => 'widgets' }, - {key => 'core.mysql.client', value => 'mysql.exe' }, - {key => 'core.mysql.registry', value => 'spliff' }, - {key => 'core.mysql.host', value => 'localhost' }, - {key => 'core.mysql.port', value => 1234 }, - {key => 'core.mysql.username', value => 'fred' }, - {key => 'core.mysql.password', value => 'barb' }, - {key => 'core.mysql.db_name', value => 'ouch' }, -]); -$cmd->sqitch->config->load; -my $core = $cmd->sqitch->config->get_section(section => 'core.mysql'); -ok $cmd->update_config, 'Update the config'; -$cmd->sqitch->config->load; -is_deeply $cmd->sqitch->config->get_section(section => 'core.mysql'), $core, - 'The core.mysql config should still be present'; -is_deeply $cmd->sqitch->config->get_section(section => 'engine.mysql'), { - target => 'widgets', - client => 'mysql.exe', - registry => 'spliff', -}, 'MySQL config should have been rewritten without deprecated keys'; - -# Try with no target. -$config->rename_section( - from => 'engine.mysql', - filename => $config->local_file, -); -$config->group_set($config->local_file, [ - {key => 'core.mysql.target', value => undef }, - {key => 'core.mysql.client', value => 'mysql.exe' }, - {key => 'core.mysql.registry', value => 'spliff' }, - {key => 'core.mysql.host', value => 'localhost' }, - {key => 'core.mysql.port', value => 1234 }, - {key => 'core.mysql.username', value => 'fred' }, - {key => 'core.mysql.password', value => 'barb' }, - {key => 'core.mysql.db_name', value => 'ouch' }, -]); -$cmd->sqitch->config->load; -$core = $cmd->sqitch->config->get_section(section => 'core.mysql'); -ok $cmd->update_config, 'Update the config again'; -$cmd->sqitch->config->load; -is_deeply $cmd->sqitch->config->get_section(section => 'core.mysql'), $core, - 'The core.mysql config should again remain'; -is_deeply $cmd->sqitch->config->get_section(section => 'engine.mysql'), { - target => 'db:mysql://fred:barb@localhost:1234/ouch', - client => 'mysql.exe', - registry => 'spliff', -}, 'MySQL config should have been rewritten with an integrated target'; - -# Try with no deprecated keys. -$config->rename_section( - from => 'engine.mysql', - filename => $config->local_file, -); -$config->group_set($config->local_file, [ - {key => 'core.mysql.client', value => 'mysql.exe' }, - {key => 'core.mysql.registry', value => 'spliff' }, - {key => 'core.mysql.host', value => undef }, - {key => 'core.mysql.port', value => undef }, - {key => 'core.mysql.username', value => undef }, - {key => 'core.mysql.password', value => undef }, - {key => 'core.mysql.db_name', value => undef }, -]); -$cmd->sqitch->config->load; -$core = $cmd->sqitch->config->get_section(section => 'core.mysql'); -ok $cmd->update_config, 'Update the config again'; -$cmd->sqitch->config->load; -is_deeply $cmd->sqitch->config->get_section(section => 'core.mysql'), $core, - 'The core.mysql config should again remain'; -is_deeply $cmd->sqitch->config->get_section(section => 'engine.mysql'), { - target => 'db:mysql:', - client => 'mysql.exe', - registry => 'spliff', -}, 'MySQL config should have been rewritten with a default target'; diff --git a/t/exasol.t b/t/exasol.t index 7291b7ffe..18fa2c39a 100644 --- a/t/exasol.t +++ b/t/exasol.t @@ -229,6 +229,15 @@ like capture_stderr { } 'App::Sqitch::X', '_capture should die when exaplus dies'; }, qr/^OMGWTF/m, 'STDERR should be emitted by _capture'; +############################################################################## +# Test unexpeted datbase error in _cid(). +$mock_exa->mock(dbh => sub { die 'OW' }); +throws_ok { $exa->initialized } qr/OW/, + 'initialized() should rethrow unexpected DB error'; +throws_ok { $exa->_cid } qr/OW/, + '_cid should rethrow unexpected DB error'; +$mock_exa->unmock('dbh'); + ############################################################################## # Test _file_for_script(). can_ok $exa, '_file_for_script'; @@ -247,6 +256,20 @@ $file->touch; # File must exist, because on Windows it gets copied. is $exa->_file_for_script($file), $tmpdir->file('foo_bar.sql'), 'File with special char should be aliased'; +# Now the alias exists, make sure _file_for_script dies if it cannot remove it. +FILE: { + my $mock_pcf = Test::MockModule->new('Path::Class::File'); + $mock_pcf->mock(remove => 0); + throws_ok { $exa->_file_for_script($file) } 'App::Sqitch::X', + 'Should get an error on failure to delete the alias'; + is $@->ident, 'exasol', 'File deletion error ident should be "exasol"'; + is $@->message, __x( + 'Cannot remove {file}: {error}', + file => $tmpdir->file('foo_bar.sql'), + error => $!, + ), 'File deletion error message should be correct'; +} + # Make sure double-quotes are escaped. WIN32: { $file = $tmpdir->file('"foo$bar".sql'); @@ -326,7 +349,8 @@ is $exa->_char2ts($dt), '2017-11-06 10:47:35', ############################################################################## # Test SQL helpers. -is $exa->_listagg_format, q{GROUP_CONCAT(%s SEPARATOR ' ')}, 'Should have _listagg_format'; +is $exa->_listagg_format, q{GROUP_CONCAT(%1$s ORDER BY %1$s SEPARATOR ' ')}, + 'Should have _listagg_format'; is $exa->_ts_default, 'current_timestamp', 'Should have _ts_default'; is $exa->_regex_op, 'REGEXP_LIKE', 'Should have _regex_op'; is $exa->_simple_from, ' FROM dual', 'Should have _simple_from'; diff --git a/t/firebird.t b/t/firebird.t index c9a975b5c..28b6e125b 100644 --- a/t/firebird.t +++ b/t/firebird.t @@ -63,6 +63,7 @@ is $fb->key, 'firebird', 'Key should be "firebird"'; is $fb->name, 'Firebird', 'Name should be "Firebird"'; is $fb->username, $ENV{ISC_USER}, 'Should have username from environment'; is $fb->password, $ENV{ISC_PASSWORD}, 'Should have password from environment'; +is $fb->_limit_default, '18446744073709551615', 'Should have _limit_default'; my $have_fb_client; if ($have_fb_driver && (my $client = try { $fb->client })) { @@ -158,7 +159,6 @@ is $@->message, __x( uri => 'db:firebird:', ), 'No dbname exception message should be correct'; - ############################################################################## # Test _run(), _capture(), and _spool(). can_ok $fb, qw(_run _capture _spool); @@ -278,18 +278,128 @@ is $dt->second, 1, 'DateTime second should be set'; is $dt->time_zone->name, 'UTC', 'DateTime TZ should be set'; ############################################################################## +# Test error checking functions. +DBI: { + local *DBI::errstr; + ok !$fb->_no_table_error, 'Should have no table error'; + ok !$fb->_no_column_error, 'Should have no column error'; + + $DBI::errstr = '-Table unknown'; + ok $fb->_no_table_error, 'Should now have table error'; + ok !$fb->_no_column_error, 'Still should have no column error'; + + $DBI::errstr = 'No such file or directory'; + ok $fb->_no_table_error, 'Should again have table error'; + ok !$fb->_no_column_error, 'Still should have no column error'; + + $DBI::errstr = '-Column unknown'; + ok !$fb->_no_table_error, 'Should again have no table error'; + ok $fb->_no_column_error, 'Should now have no column error'; +} +############################################################################## +# Test database creation failure. +DBFAIL: { + my $mock = Test::MockModule->new($CLASS); + $mock->mock(initialized => 0); + $mock->mock(use_driver => 1); + my $fbmock = Test::MockModule->new('DBD::Firebird', no_auto => 1); + $fbmock->mock(create_database => sub { die 'Creation failed' }); + throws_ok { $fb->initialize } 'App::Sqitch::X', + 'Should get an error from initialize'; + is $@->ident, 'firebird', 'No creattion exception ident should be "firebird"'; + my $msg = __x( + 'Cannot create database {database}: {error}', + database => $fb->connection_string($fb->registry_uri), + error => 'Creation failed', + ); + like $@->message, qr{^\Q$msg\E}, 'Creation exception message should be correct'; +} + +############################################################################## +# Test various database connection and error-handling logic. +DBH: { + # Need to mock DBH. + my $dbh = DBI->connect('dbi:Mem:', undef, undef, {}); + my $mock_engine = Test::MockModule->new($CLASS); + $mock_engine->mock(dbh => $dbh); + $mock_engine->mock(registry_uri => URI->new('db:firebird:foo.fdb')); + my $mock_dbd = Test::MockModule->new(ref $dbh, no_auto => 1); + my ($disconnect, $clear); + $mock_dbd->mock(disconnect => sub { $disconnect = 1 }); + $mock_engine->mock(_clear_dbh => sub { $clear = 1 }); + my $run; + $mock_sqitch->mock(run => sub { $run = 1 }); + + # Test that upgrading disconnects from a local database before upgrading. + ok $fb->run_upgrade('somefile'), 'Run the upgrade'; + ok $disconnect, 'Should have disconnected'; + ok $clear, 'Should have cleared the database handle'; + ok $run, 'Should have run a command'; + $mock_sqitch->unmock('run'); + + # Test that _cid propagates an unexpected error from DBI. + local *DBI::err; + $DBI::err = 0; + $mock_engine->mock(dbh => sub { die 'Oops' }); + throws_ok { $fb->_cid('ASC', 0, 'foo') } qr/^Oops/, + '_cid should propagate unexpected error'; + + # But it should just return for error code -902. + $DBI::err = -902; + lives_ok { $fb->_cid('ASC', 0, 'foo') } + '_cid should just return on error code -902'; + + # Test that current_state returns on no table error. + local *DBI::errstr; + $DBI::errstr = '-Table unknown'; + $mock_engine->mock(initialized => 0); + lives_ok { $fb->current_state('foo') } + 'current_state should return on no table error'; + + # But it should die if it's not a table error. + $DBI::errstr = 'Some other error'; + throws_ok { $fb->current_state('foo') } qr/^Oops/, + 'current_state should propagate unexpected error'; + + # Make sure change_id_for returns undef when no useful params. + $mock_engine->mock(dbh => $dbh); + is $fb->change_id_for(project => 'foo'), undef, + 'Should get undef from change_id_for when no useful params'; +} + +# Make sure defaul_client croaks when it finds no client. +FSPEC: { + # Give it an invalid fbsql file to find. + my $tmpdir = tempdir(CLEANUP => 1); + my $tmp = Path::Class::Dir->new("$tmpdir"); + my $iswin = App::Sqitch::ISWIN || $^O eq 'cygwin'; + my $fbsql = $tmp->file('fbsql' . ($iswin ? '.exe' : '')); + $fbsql->touch; + chmod '0755', $fbsql unless $iswin; + + my $fs_mock = Test::MockModule->new('File::Spec'); + $fs_mock->mock(path => sub { $tmp }); + throws_ok { $fb->default_client } 'App::Sqitch::X', + 'Should get error when no client found'; + is $@->ident, 'firebird', 'Client exception ident should be "firebird"'; + is $@->message, __( + 'Unable to locate Firebird ISQL; set "engine.firebird.client" via sqitch config' + ), 'Client exception message should be correct'; +} + +############################################################################## # Can we do live tests? my ($data_dir, $fb_version, @cleanup) = ($tmpdir); my $id = DBIEngineTest->randstr; -my ($reg1, $reg2) = map { $_ . $id } qw(__sqitchtest__ __metasqitch); +my ($reg1, $reg2) = map { $_ . $id } qw(__sqitchreg_ __metasqitch_); my $err = try { return unless $have_fb_driver; if ($uri->dbname) { $data_dir = dirname $uri->dbname; # Assumes local OS semantics. } else { # Assume we're running locally and create the database. - my $dbpath = catfile($tmpdir, $reg1); + my $dbpath = catfile($tmpdir, "__sqitchtest__$id"); $data_dir = $tmpdir; $uri->dbname($dbpath); DBD::Firebird->create_database({ @@ -299,8 +409,10 @@ my $err = try { character_set => 'UTF8', page_size => 16384, }); + # We created this database, we need to clean it up. @cleanup = ($dbpath); } + # Try to connect. my $dbh = DBI->connect($uri->dbi_dsn, $uri->user, $uri->password, { PrintError => 0, @@ -310,7 +422,9 @@ my $err = try { $fb_version = $dbh->selectcol_arrayref(q{ SELECT rdb$get_context('SYSTEM', 'ENGINE_VERSION') FROM rdb$database - })->[0]; + })->[0]; + + # We will need to clean up the registry DBs we create. push @cleanup => map { catfile $data_dir, $_ } $reg1, $reg2; return undef; } catch { @@ -320,6 +434,7 @@ my $err = try { END { return if $ENV{CI}; # No need to clean up in CI environment. foreach my $dbname (@cleanup) { + next unless -e $dbname; $uri->dbname($dbname); my $dsn = $uri->dbi_dsn . q{;ib_dialect=3;ib_charset=UTF8}; my $dbh = DBI->connect($dsn, $uri->user, $uri->password, { diff --git a/t/init.t b/t/init.t index 307ee56c3..c875581cb 100644 --- a/t/init.t +++ b/t/init.t @@ -4,8 +4,8 @@ use strict; use warnings; use 5.010; use utf8; -use Test::More tests => 187; -#use Test::More 'no_plan'; +use Test::More tests => 196; +# use Test::More 'no_plan'; use App::Sqitch; use Locale::TextDomain qw(App-Sqitch); use Path::Class; @@ -613,6 +613,42 @@ for my $bad (@bad_names) { ), qq{Bad project "$bad" error message should be correct}; } +# Make sure that config_target will add the URI if passed (even though it's not +# clear what it's used for, if at all). +isa_ok $target = $init->config_target( + name => 'custom', + uri => URI->new('db:pg:actually'), +), 'App::Sqitch::Target', 'Custom URI target'; +is $target->uri, URI->new('db:pg:actually'), 'Shoudl have the custom URI'; +is $target->name, 'custom', 'Should have the custom name'; + +# Handle errors. +FSERR: { + # Make mkpath to insert an error. + my $mock = Test::MockModule->new('File::Path'); + $mock->mock( mkpath => sub { + my ($file, $p) = @_; + ${ $p->{error} } = [{ $file => 'Permission denied yo'}]; + return; + }); + + throws_ok { $init->mkdirs('foo') } 'App::Sqitch::X', + 'Should fail on permission issue'; + is $@->ident, 'init', 'Permission error should have ident "init"'; + is $@->message, __x( + 'Error creating {path}: {error}', + path => 'foo', + error => 'Permission denied yo', + ), 'The permission error should be formatted properly'; + + # Try an error with no path. + throws_ok { $init->mkdirs('') } 'App::Sqitch::X', + 'Should fail on nonexistent dir name'; + is $@->ident, 'init', 'Nonexistant path error should have ident "init"'; + is $@->message, 'Permission denied yo', + 'Nonexistant path error should be the message'; +} + ############################################################################## # Bring it all together, yo. $conf_file->remove; diff --git a/t/lib/DBIEngineTest.pm b/t/lib/DBIEngineTest.pm index 7ca28c07c..1385305f2 100644 --- a/t/lib/DBIEngineTest.pm +++ b/t/lib/DBIEngineTest.pm @@ -961,10 +961,8 @@ sub run { is $engine->latest_change_id(3), $change->id, 'Should get "users" offset 3 from latest'; $state = $engine->current_state; - # MySQL's group_concat() and Oracle's collect() do not by default sort - # by row order, alas. - $state->{tags} = [ sort @{ $state->{tags} } ] - if $class =~ /::(?:mysql|oracle)$/; + # Oracle's collect() does not by default sort by row order, alas. + $state->{tags} = [ sort @{ $state->{tags} } ] if $class =~ /::oracle$/; is_deeply $state, { project => 'engine', change_id => $barney->id, @@ -1724,7 +1722,7 @@ sub run { ###################################################################### # Test try_lock() and wait_lock(). - if (my $sql = $p{lock_sql}) { + if (my $sql = ($p{lock_sql} || sub {})->($engine)) { ok !$engine->dbh->selectcol_arrayref($sql->{is_locked})->[0], 'Should not be locked'; ok $engine->try_lock, 'Try lock'; diff --git a/t/lib/upgradable_registries/cockroach.sql b/t/lib/upgradable_registries/cockroach.sql new file mode 100644 index 000000000..1703b5626 --- /dev/null +++ b/t/lib/upgradable_registries/cockroach.sql @@ -0,0 +1,136 @@ +SET client_min_messages = warning; +CREATE SCHEMA IF NOT EXISTS :"registry"; + +COMMENT ON SCHEMA :"registry" IS 'Sqitch database deployment metadata v1.0.'; + +CREATE TABLE :"registry".releases ( + version REAL PRIMARY KEY, + installed_at TIMESTAMPTZ NOT NULL DEFAULT clock_timestamp(), + installer_name TEXT NOT NULL, + installer_email TEXT NOT NULL +); + +COMMENT ON TABLE :"registry".releases IS 'Sqitch registry releases.'; +COMMENT ON COLUMN :"registry".releases.version IS 'Version of the Sqitch registry.'; +COMMENT ON COLUMN :"registry".releases.installed_at IS 'Date the registry release was installed.'; +COMMENT ON COLUMN :"registry".releases.installer_name IS 'Name of the user who installed the registry release.'; +COMMENT ON COLUMN :"registry".releases.installer_email IS 'Email address of the user who installed the registry release.'; + +CREATE TABLE :"registry".projects ( + project TEXT PRIMARY KEY, + uri TEXT NULL UNIQUE, + created_at TIMESTAMPTZ NOT NULL DEFAULT clock_timestamp(), + creator_name TEXT NOT NULL, + creator_email TEXT NOT NULL +); + +COMMENT ON TABLE :"registry".projects IS 'Sqitch projects deployed to this database.'; +COMMENT ON COLUMN :"registry".projects.project IS 'Unique Name of a project.'; +COMMENT ON COLUMN :"registry".projects.uri IS 'Optional project URI'; +COMMENT ON COLUMN :"registry".projects.created_at IS 'Date the project was added to the database.'; +COMMENT ON COLUMN :"registry".projects.creator_name IS 'Name of the user who added the project.'; +COMMENT ON COLUMN :"registry".projects.creator_email IS 'Email address of the user who added the project.'; + +CREATE TABLE :"registry".changes ( + change_id TEXT PRIMARY KEY, + change TEXT NOT NULL, + project TEXT NOT NULL REFERENCES :"registry".projects(project) ON UPDATE CASCADE, + note TEXT NOT NULL DEFAULT '', + committed_at TIMESTAMPTZ NOT NULL DEFAULT clock_timestamp(), + committer_name TEXT NOT NULL, + committer_email TEXT NOT NULL, + planned_at TIMESTAMPTZ NOT NULL, + planner_name TEXT NOT NULL, + planner_email TEXT NOT NULL +); + +COMMENT ON TABLE :"registry".changes IS 'Tracks the changes currently deployed to the database.'; +COMMENT ON COLUMN :"registry".changes.change_id IS 'Change primary key.'; +COMMENT ON COLUMN :"registry".changes.change IS 'Name of a deployed change.'; +COMMENT ON COLUMN :"registry".changes.project IS 'Name of the Sqitch project to which the change belongs.'; +COMMENT ON COLUMN :"registry".changes.note IS 'Description of the change.'; +COMMENT ON COLUMN :"registry".changes.committed_at IS 'Date the change was deployed.'; +COMMENT ON COLUMN :"registry".changes.committer_name IS 'Name of the user who deployed the change.'; +COMMENT ON COLUMN :"registry".changes.committer_email IS 'Email address of the user who deployed the change.'; +COMMENT ON COLUMN :"registry".changes.planned_at IS 'Date the change was added to the plan.'; +COMMENT ON COLUMN :"registry".changes.planner_name IS 'Name of the user who planed the change.'; +COMMENT ON COLUMN :"registry".changes.planner_email IS 'Email address of the user who planned the change.'; + +CREATE TABLE :"registry".tags ( + tag_id TEXT PRIMARY KEY, + tag TEXT NOT NULL, + project TEXT NOT NULL REFERENCES :"registry".projects(project) ON UPDATE CASCADE, + change_id TEXT NOT NULL REFERENCES :"registry".changes(change_id) ON UPDATE CASCADE, + note TEXT NOT NULL DEFAULT '', + committed_at TIMESTAMPTZ NOT NULL DEFAULT clock_timestamp(), + committer_name TEXT NOT NULL, + committer_email TEXT NOT NULL, + planned_at TIMESTAMPTZ NOT NULL, + planner_name TEXT NOT NULL, + planner_email TEXT NOT NULL, + UNIQUE(project, tag) +); + +COMMENT ON TABLE :"registry".tags IS 'Tracks the tags currently applied to the database.'; +COMMENT ON COLUMN :"registry".tags.tag_id IS 'Tag primary key.'; +COMMENT ON COLUMN :"registry".tags.tag IS 'Project-unique tag name.'; +COMMENT ON COLUMN :"registry".tags.project IS 'Name of the Sqitch project to which the tag belongs.'; +COMMENT ON COLUMN :"registry".tags.change_id IS 'ID of last change deployed before the tag was applied.'; +COMMENT ON COLUMN :"registry".tags.note IS 'Description of the tag.'; +COMMENT ON COLUMN :"registry".tags.committed_at IS 'Date the tag was applied to the database.'; +COMMENT ON COLUMN :"registry".tags.committer_name IS 'Name of the user who applied the tag.'; +COMMENT ON COLUMN :"registry".tags.committer_email IS 'Email address of the user who applied the tag.'; +COMMENT ON COLUMN :"registry".tags.planned_at IS 'Date the tag was added to the plan.'; +COMMENT ON COLUMN :"registry".tags.planner_name IS 'Name of the user who planed the tag.'; +COMMENT ON COLUMN :"registry".tags.planner_email IS 'Email address of the user who planned the tag.'; + +CREATE TABLE :"registry".dependencies ( + change_id TEXT NOT NULL REFERENCES :"registry".changes(change_id) ON UPDATE CASCADE ON DELETE CASCADE, + type TEXT NOT NULL, + dependency TEXT NOT NULL, + dependency_id TEXT NULL REFERENCES :"registry".changes(change_id) ON UPDATE CASCADE CHECK ( + (type = 'require' AND dependency_id IS NOT NULL) + OR (type = 'conflict' AND dependency_id IS NULL) + ), + PRIMARY KEY (change_id, dependency) +); + +COMMENT ON TABLE :"registry".dependencies IS 'Tracks the currently satisfied dependencies.'; +COMMENT ON COLUMN :"registry".dependencies.change_id IS 'ID of the depending change.'; +COMMENT ON COLUMN :"registry".dependencies.type IS 'Type of dependency.'; +COMMENT ON COLUMN :"registry".dependencies.dependency IS 'Dependency name.'; +COMMENT ON COLUMN :"registry".dependencies.dependency_id IS 'Change ID the dependency resolves to.'; + +CREATE TABLE :"registry".events ( + event TEXT NOT NULL CHECK (event IN ('deploy', 'revert', 'fail')), + change_id TEXT NOT NULL, + change TEXT NOT NULL, + project TEXT NOT NULL REFERENCES :"registry".projects(project) ON UPDATE CASCADE, + note TEXT NOT NULL DEFAULT '', + requires TEXT[] NOT NULL DEFAULT '{}', + conflicts TEXT[] NOT NULL DEFAULT '{}', + tags TEXT[] NOT NULL DEFAULT '{}', + committed_at TIMESTAMPTZ NOT NULL DEFAULT clock_timestamp(), + committer_name TEXT NOT NULL, + committer_email TEXT NOT NULL, + planned_at TIMESTAMPTZ NOT NULL, + planner_name TEXT NOT NULL, + planner_email TEXT NOT NULL, + PRIMARY KEY (change_id, committed_at) +); + +COMMENT ON TABLE :"registry".events IS 'Contains full history of all deployment events.'; +COMMENT ON COLUMN :"registry".events.event IS 'Type of event.'; +COMMENT ON COLUMN :"registry".events.change_id IS 'Change ID.'; +COMMENT ON COLUMN :"registry".events.change IS 'Change name.'; +COMMENT ON COLUMN :"registry".events.project IS 'Name of the Sqitch project to which the change belongs.'; +COMMENT ON COLUMN :"registry".events.note IS 'Description of the change.'; +COMMENT ON COLUMN :"registry".events.requires IS 'Array of the names of required changes.'; +COMMENT ON COLUMN :"registry".events.conflicts IS 'Array of the names of conflicting changes.'; +COMMENT ON COLUMN :"registry".events.tags IS 'Tags associated with the change.'; +COMMENT ON COLUMN :"registry".events.committed_at IS 'Date the event was committed.'; +COMMENT ON COLUMN :"registry".events.committer_name IS 'Name of the user who committed the event.'; +COMMENT ON COLUMN :"registry".events.committer_email IS 'Email address of the user who committed the event.'; +COMMENT ON COLUMN :"registry".events.planned_at IS 'Date the event was added to the plan.'; +COMMENT ON COLUMN :"registry".events.planner_name IS 'Name of the user who planed the change.'; +COMMENT ON COLUMN :"registry".events.planner_email IS 'Email address of the user who plan planned the change.'; diff --git a/t/lib/upgradable_registries/pg.sql b/t/lib/upgradable_registries/pg.sql index 59f1d3978..a0f42da20 100644 --- a/t/lib/upgradable_registries/pg.sql +++ b/t/lib/upgradable_registries/pg.sql @@ -10,7 +10,7 @@ CREATE TABLE :"registry".releases ( installed_at TIMESTAMPTZ NOT NULL DEFAULT clock_timestamp(), installer_name TEXT NOT NULL, installer_email TEXT NOT NULL -); +):tableopts; COMMENT ON TABLE :"registry".releases IS 'Sqitch registry releases.'; COMMENT ON COLUMN :"registry".releases.version IS 'Version of the Sqitch registry.'; diff --git a/t/mysql.t b/t/mysql.t index f286c1fe6..13375274a 100644 --- a/t/mysql.t +++ b/t/mysql.t @@ -13,10 +13,12 @@ use 5.010; use Test::More; use App::Sqitch; use App::Sqitch::Target; +use Test::File::Contents; use Test::MockModule; use Path::Class; use Try::Tiny; use Test::Exception; +use List::MoreUtils qw(firstidx); use Locale::TextDomain qw(App-Sqitch); use File::Temp 'tempdir'; use lib 't/lib'; @@ -108,10 +110,11 @@ ENV: { } ############################################################################## -# Make sure config settings override defaults. +# Make sure config settings override defaults and the password is set or removed +# as appropriate. $config->update( 'engine.mysql.client' => '/path/to/mysql', - 'engine.mysql.target' => 'db:mysql://foo.com/widgets', + 'engine.mysql.target' => 'db:mysql://me:pwd@foo.com/widgets', 'engine.mysql.registry' => 'meta', ); my $mysql_version = 'mysql Ver 15.1 Distrib 10.0.15-MariaDB'; @@ -123,20 +126,23 @@ $target = App::Sqitch::Target->new(sqitch => $sqitch); ok $mysql = $CLASS->new(sqitch => $sqitch, target => $target), 'Create another mysql'; is $mysql->client, '/path/to/mysql', 'client should be as configured'; -is $mysql->uri->as_string, 'db:mysql://foo.com/widgets', +is $mysql->uri->as_string, 'db:mysql://me:pwd@foo.com/widgets', 'URI should be as configured'; -is $mysql->target->name, $mysql->uri->as_string, 'target name should be the URI'; -is $mysql->destination, $mysql->uri->as_string, 'destination should be the URI'; +is $mysql->target->name, 'db:mysql://me:@foo.com/widgets', + 'target name should be the URI without the password'; +is $mysql->destination, 'db:mysql://me:@foo.com/widgets', + 'destination should be the URI without the password'; is $mysql->registry, 'meta', 'registry should be as configured'; -is $mysql->registry_uri->as_string, 'db:mysql://foo.com/meta', +is $mysql->registry_uri->as_string, 'db:mysql://me:pwd@foo.com/meta', 'Sqitch DB URI should be the same as uri but with DB name "meta"'; -is $mysql->registry_destination, $mysql->registry_uri->as_string, - 'registry_destination should be the sqitch DB URL'; +is $mysql->registry_destination, 'db:mysql://me:@foo.com/meta', + 'registry_destination should be the sqitch DB URL without the password'; is_deeply [$mysql->mysql], [ '/path/to/mysql', - '--user', $sqitch->sysuser, + '--user', 'me', '--database', 'widgets', '--host', 'foo.com', + '--password=pwd', @std_opts ], 'mysql command should be configured'; @@ -360,7 +366,8 @@ is $dt->time_zone->name, 'UTC', 'DateTime TZ should be set'; ############################################################################## # Test SQL helpers. -is $mysql->_listagg_format, q{GROUP_CONCAT(%s SEPARATOR ' ')}, 'Should have _listagg_format'; +is $mysql->_listagg_format, q{GROUP_CONCAT(%1$s ORDER BY %1$s SEPARATOR ' ')}, + 'Should have _listagg_format'; is $mysql->_regex_op, 'REGEXP', 'Should have _regex_op'; is $mysql->_simple_from, '', 'Should have _simple_from'; is $mysql->_limit_default, '18446744073709551615', 'Should have _limit_default'; @@ -377,11 +384,15 @@ SECS: { is $my51->_ts_default, 'utc_timestamp', 'Should have _ts_default without fractional seconds on 5.1'; - $dbh->{mysql_serverversion} = 50604; + $dbh->{mysql_serverversion} = 50304; $dbh->{mysql_serverinfo} = 'Something about MariaDB man'; my $maria = $CLASS->new(sqitch => $sqitch, target => $target); is $maria->_ts_default, 'utc_timestamp', - 'Should have _ts_default without fractional seconds on mariadb'; + 'Should have _ts_default without fractional seconds on early mariadb'; + + $dbh->{mysql_serverversion} = 50305; + is $mysql->_ts_default, 'utc_timestamp(6)', + 'Should have _ts_default with fractional secondson mariadb 5.03.05'; } DBI: { @@ -424,6 +435,136 @@ is_deeply [$mysql->_regex_expr('corn', 'Obama$')], ['corn REGEXP ?', 'Obama$'], 'Should use REGEXP for regex expr'; +############################################################################## +# Test unexpeted datbase error in initialized() and _cid(). +MOCKDBH: { + my $mock = Test::MockModule->new($CLASS); + $mock->mock(dbh => sub { die 'OW' }); + throws_ok { $mysql->initialized } qr/OW/, + 'initialized() should rethrow unexpected DB error'; + throws_ok { $mysql->_cid } qr/OW/, + '_cid should rethrow unexpected DB error'; +} + +############################################################################## +# Test _prepare_to_log(). +PREPLOG: { + my $mock = Test::MockModule->new($CLASS); + my $fracsec; + $mock->mock(_fractional_seconds => sub { $fracsec }); + + # Start with fractional seconds detected. + $fracsec = 1; + is $mysql, $mysql->_prepare_to_log('atable', undef), + 'Should just get self when fractional seconds supported'; + + # Now try with fractional seconds unsupported by the database. + $fracsec = 0; + + # Need to mock the database handle. + my $dbh = DBI->connect('dbi:Mem:', undef, undef, {}); + $mock->mock(dbh => $dbh); + my $mock_dbh = Test::MockModule->new(ref $dbh, no_auto => 1); + my @prepared; + $mock_dbh->mock(prepare => sub { shift; @prepared = @_ }); + my @results = ([1], [0]); + $mock_dbh->mock(selectcol_arrayref => sub { shift @results }); + + # Mock sleep, too. + my $mock_thr = Test::MockModule->new('Time::HiRes'); + my @slept; + $mock_thr->mock(sleep => sub { push @slept, shift } ); + + # We need to pass in a real change. + my $plan = App::Sqitch::Plan->new( + sqitch => $sqitch, + target => $target, + 'project' => 'mysql', + ); + my $change = App::Sqitch::Plan::Change->new( + name => 'mysql_test', + plan => $plan, + ); + + # Make sure it sleeps once. + lives_ok { $mysql->_prepare_to_log('atable', $change) } + 'Should get no error from _prepare_to_log'; + + # Check the stuff that was passed. + is_deeply \@prepared, [qq{ + SELECT UNIX_TIMESTAMP(committed_at) >= UNIX_TIMESTAMP() + FROM atable + WHERE project = ? + ORDER BY committed_at DESC + LIMIT 1 + }], 'Should have prepared the statement comparing times'; + is_deeply \@results, [], 'Results should have been returned'; + is_deeply \@slept, [0.1], 'Should have slept once'; +} + +############################################################################## +# Test run_upgrade(). +UPGRADE: { + my $mock = Test::MockModule->new($CLASS); + my $fracsec; + my $version = 50500; + $mock->mock(_fractional_seconds => sub { $fracsec }); + $mock->mock(dbh => sub { { mysql_serverversion => $version } }); + + # Mock run. + my @run; + $mock_sqitch->mock(run => sub { shift; @run = @_ }); + + # Mock File::Temp so we hang on to the file. + my $mock_ft = Test::MockModule->new('File::Temp'); + my $tmp_fh; + my $ft_new; + $mock_ft->mock(new => sub { $tmp_fh = 'File::Temp'->$ft_new() }); + $ft_new = $mock_ft->original('new'); + + # Assemble the expected command. + my @cmd = $mysql->mysql; + $cmd[1 + firstidx { $_ eq '--database' } @cmd ] = $mysql->registry; + my $fn = file($INC{'App/Sqitch/Engine/mysql.pm'})->dir->file('mysql.sql'); + + # Test with fractional seconds supported. + $fracsec = 1; + ok $mysql->run_upgrade($fn), 'Run the upgrade'; + is $tmp_fh, undef, 'Should not have created a temp file'; + is_deeply \@run, [@cmd, $mysql->_source($fn)], + 'It should have run the unchanged file'; + + # Now disable fractional seconds. + $fracsec = 0; + ok $mysql->run_upgrade($fn), 'Run the upgrade again'; + ok $tmp_fh, 'Should have created a temp file'; + is_deeply \@run, [@cmd, $mysql->_source($tmp_fh)], + 'It should have run the temp file'; + + # Make sure the file was changed to remove precision from datetimes. + file_contents_unlike $tmp_fh, qr/DATETIME\(\d+\)/, + 'Should have removed datetime precision'; + file_contents_like $tmp_fh, qr/-- ## BEGIN 5\.5/, + 'Should not have removed MySQL 5.5-requiring block BEGIN'; + file_contents_like $tmp_fh, qr/-- ## END 5\.5/, + 'Should not have removed MySQL 5.5-requiring block END'; + + # Now try MySQL 5.4. + $version = 50400; + $tmp_fh = undef; + ok $mysql->run_upgrade($fn), 'Run the upgrade on 5.4'; + ok $tmp_fh, 'Should have created another temp file'; + is_deeply \@run, [@cmd, $mysql->_source($tmp_fh)], + 'It should have the new temp file'; + + file_contents_unlike $tmp_fh, qr/-- ## BEGIN 5\.5/, + 'Should have removed MySQL 5.5-requiring block BEGIN'; + file_contents_unlike $tmp_fh, qr/-- ## END 5\.5/, + 'Should have removed MySQL 5.5-requiring block END'; + + $mock_sqitch->unmock_all; +} + ############################################################################## # Can we do live tests? my $dbh; @@ -462,8 +603,8 @@ my $err = try { unless $dbh->{mysql_serverversion} >= 50300; } else { - die "MySQL >= 50000 required; this is $dbh->{mysql_serverversion}\n" - unless $dbh->{mysql_serverversion} >= 50000; + die "MySQL >= 50100 required; this is $dbh->{mysql_serverversion}\n" + unless $dbh->{mysql_serverversion} >= 50100; } $dbh->do("CREATE DATABASE $db"); @@ -520,19 +661,19 @@ DBIEngineTest->run( like $sql_mode, qr/\b\Q$mode\E\b/i, "sql_mode should include $mode"; } }, - lock_sql => { - is_locked => q{SELECT is_used_lock('sqitch working')}, - try_lock => q{SELECT get_lock('sqitch working', 0)}, - wait_time => 1, # get_lock() does not support sub-second precision, apparently. - async_free => 1, - free_lock => 'SELECT ' . ($dbh ? do { - # MySQL 5.5-5.6 and Maria 10.0-10.4 prefer release_lock(), while - # 5.7+ and 10.5+ prefer release_all_locks(). - $dbh->selectrow_arrayref('SELECT version()')->[0] =~ /^(?:5\.[56]|10\.[0-4])/ - ? q{release_lock('sqitch working')} - : 'release_all_locks()' - } : ''), - }, + lock_sql => sub { return { + is_locked => q{SELECT is_used_lock('sqitch working')}, + try_lock => q{SELECT get_lock('sqitch working', 0)}, + wait_time => 1, # get_lock() does not support sub-second precision, apparently. + async_free => 1, + free_lock => 'SELECT ' . ($dbh ? do { + # MySQL 5.5-5.6 and Maria 10.0-10.4 prefer release_lock(), while + # 5.7+ and 10.5+ prefer release_all_locks(). + $dbh->selectrow_arrayref('SELECT version()')->[0] =~ /^(?:5\.[56]|10\.[0-4])/ + ? q{release_lock('sqitch working')} + : 'release_all_locks()' + } : ''), + } }, ); done_testing; diff --git a/t/options.t b/t/options.t index 752ff3e71..16a023045 100644 --- a/t/options.t +++ b/t/options.t @@ -6,6 +6,7 @@ use utf8; use Test::More; use Test::MockModule; use Test::Exception; +use Test::Exit; use Capture::Tiny 0.12 ':all'; use Locale::TextDomain qw(App-Sqitch); use lib 't/lib'; @@ -134,6 +135,24 @@ HELP: { 'Should have been manned'; } +# Make sure we get the version and etc path. +EXITING: { + my $mocker = Test::MockModule->new($CLASS); + my @emit; + $mocker->mock(emit => sub { shift; push @emit => @_ }); + $mocker->mock(VERSION => sub { '1.2.3-testing' }); + exits_ok { $CLASS->_parse_core_opts(['--version']) } + 'Should have exited on --version'; + is_deeply \@emit, ['options.t', ' (', $CLASS, ') ', '1.2.3-testing'], + 'Should have emitted the version'; + + @emit = (); + exits_ok { $CLASS->_parse_core_opts(['--etc-path']) } + 'Should have exited on --etc-path'; + is_deeply \@emit, [App::Sqitch::Config->system_dir], + 'Should have emitted the etc path'; +} + # Silence warnings. my $mock = Test::MockModule->new($CLASS); $mock->mock(warn => undef); diff --git a/t/oracle.t b/t/oracle.t index ae88142c5..a5aabd22d 100644 --- a/t/oracle.t +++ b/t/oracle.t @@ -400,6 +400,20 @@ $file->touch; # File must exist, because on Windows it gets copied. is $ora->_file_for_script($file), $tmpdir->file('foo_bar.sql'), 'File with special char should be aliased'; +# Now the alias exists, make sure _file_for_script dies if it cannot remove it. +FILE: { + my $mock_pcf = Test::MockModule->new('Path::Class::File'); + $mock_pcf->mock(remove => 0); + throws_ok { $ora->_file_for_script($file) } 'App::Sqitch::X', + 'Should get an error on failure to delete the alias'; + is $@->ident, 'oracle', 'File deletion error ident should be "oracle"'; + is $@->message, __x( + 'Cannot remove {file}: {error}', + file => $tmpdir->file('foo_bar.sql'), + error => $!, + ), 'File deletion error message should be correct'; +} + # Make sure double-quotes are escaped. WIN32: { $file = $tmpdir->file('"foo$bar".sql'); @@ -410,6 +424,15 @@ WIN32: { 'File with special char and quotes should be aliased'; } +############################################################################## +# Test unexpeted datbase error in _cid(). +$mock_ora->mock(dbh => sub { die 'OW' }); +throws_ok { $ora->initialized } qr/OW/, + 'initialized() should rethrow unexpected DB error'; +throws_ok { $ora->_cid } qr/OW/, + '_cid should rethrow unexpected DB error'; +$mock_ora->unmock('dbh'); + ############################################################################## # Test file and handle running. my @run; @@ -547,6 +570,34 @@ is_deeply $ora->_log_conflicts_param($change), [qw(aaa bbb ccc)], $mock_change->unmock_all; +############################################################################## +# Test _change_id_in() +can_ok $CLASS, qw(_change_id_in); +my $change_id_in = $CLASS->can('_change_id_in'); +is $change_id_in->(0), '', 'Should get empty string for 0 change IDs'; +is $change_id_in->(1), 'change_id IN (?)', + 'Should get single param for 1 change ID'; +is $change_id_in->(3), 'change_id IN (?, ?, ?)', + 'Should get 3 params for 3 change IDs'; +for my $count (10, 32, 50, 200, 250) { + is $change_id_in->($count), + 'change_id IN (' . join(', ' => ('?') x $count) . ')', + "Should get $count params for $count change IDs"; +} + +# Make sure we get multiple IN clauses for over 250 IDs. +my $in_group = 'change_id IN (' . join(', ' => ('?') x 250) . ')'; +is $change_id_in->(251), "$in_group OR change_id IN (?)", + 'Should get 250 and 1 groups for 251 IDs'; +is $change_id_in->(253), "$in_group OR change_id IN (?, ?, ?)", + 'Should get 250 and 3 groups for 253 IDs'; +is $change_id_in->(502), "$in_group OR $in_group OR change_id IN (?, ?)", + 'Should get 250, 240, and 2 groups for 503 IDs'; +is $change_id_in->(1042), join( + ' OR ', $in_group, $in_group, $in_group, $in_group, + 'change_id IN (' . join(', ' => ('?') x 42) . ')' +), 'Should get 4 x 250 and 42 groups for 1042 IDs'; + ############################################################################## # Can we do live tests? if (App::Sqitch::ISWIN && eval { require Win32::API }) { diff --git a/t/pg.t b/t/pg.t index 17ebf73d8..a4fef6d3a 100644 --- a/t/pg.t +++ b/t/pg.t @@ -23,12 +23,14 @@ use 5.010; use Test::More 0.94; use Test::MockModule; use Test::Exception; +use Test::File::Contents; use Locale::TextDomain qw(App-Sqitch); use Capture::Tiny 0.12 qw(:all); use Try::Tiny; use App::Sqitch; use App::Sqitch::Target; use App::Sqitch::Plan; +use Path::Class; use lib 't/lib'; use DBIEngineTest; use TestConfig; @@ -79,7 +81,7 @@ my @std_opts = ( '--set' => 'registry=sqitch', ); my $sysuser = $sqitch->sysuser; -is_deeply [$pg->psql], [$client, @std_opts], +is_deeply [$pg->psql], [$client, '--dbname', 'port=5432', @std_opts], 'psql command should be conninfo, and std opts-only'; isa_ok $pg = $CLASS->new(sqitch => $sqitch, target => $target), $CLASS; @@ -87,9 +89,10 @@ ok $pg->set_variables(foo => 'baz', whu => 'hi there', yo => 'stellar'), 'Set some variables'; is_deeply [$pg->psql], [ $client, - '--set' => 'foo=baz', - '--set' => 'whu=hi there', - '--set' => 'yo=stellar', + '--dbname' => 'port=5432', + '--set' => 'foo=baz', + '--set' => 'whu=hi there', + '--set' => 'yo=stellar', @std_opts, ], 'Variables should be passed to psql via --set'; @@ -138,12 +141,12 @@ is $pg->registry, 'meta', 'registry should be as configured'; is_deeply [$pg->psql], [ '/path/to/psql', '--dbname', - "dbname=try host=localhost connect_timeout=5 sslmode=disable", + "dbname=try host=localhost port=5432 connect_timeout=5 sslmode=disable", @std_opts], 'psql command should be configured from URI config'; ############################################################################## -# Test _run(), _capture(), and _spool(). -can_ok $pg, qw(_run _capture _spool); +# Test _run(), _capture(), _spool(), and _probe(). +can_ok $pg, qw(_run _capture _spool _probe); my $mock_sqitch = Test::MockModule->new('App::Sqitch'); my (@run, $exp_pass); $mock_sqitch->mock(run => sub { @@ -181,6 +184,18 @@ $mock_sqitch->mock(spool => sub { } }); +my @probe; +$mock_sqitch->mock(probe => sub { + local $Test::Builder::Level = $Test::Builder::Level + 2; + shift; + @probe = @_; + if (defined $exp_pass) { + is $ENV{PGPASSWORD}, $exp_pass, qq{PGPASSWORD should be "$exp_pass"}; + } else { + ok !exists $ENV{PGPASSWORD}, 'PGPASSWORD should not exist'; + } +}); + $target->uri->password('s3cr3t'); $exp_pass = 's3cr3t'; ok $pg->_run(qw(foo bar baz)), 'Call _run'; @@ -195,6 +210,9 @@ ok $pg->_capture(qw(foo bar baz)), 'Call _capture'; is_deeply \@capture, [$pg->psql, qw(foo bar baz)], 'Command should be passed to capture()'; +ok $pg->_probe(qw(hi there)), 'Call _probe'; +is_deeply \@probe, [$pg->psql, qw(hi there)]; + # Without password. $target = App::Sqitch::Target->new( sqitch => $sqitch ); ok $pg = $CLASS->new(sqitch => $sqitch, target => $target), @@ -212,6 +230,9 @@ ok $pg->_capture(qw(foo bar baz)), 'Call _capture again'; is_deeply \@capture, [$pg->psql, qw(foo bar baz)], 'Command should be passed to capture() again'; +ok $pg->_probe(qw(go there)), 'Call _probe again'; +is_deeply \@probe, [$pg->psql, qw(go there)]; + ############################################################################## # Test file and handle running. ok $pg->run_file('foo/bar.sql'), 'Run foo/bar.sql'; @@ -237,7 +258,7 @@ $mock_sqitch->unmock_all; ############################################################################## # Test DateTime formatting stuff. ok my $ts2char = $CLASS->can('_ts2char_format'), "$CLASS->can('_ts2char_format')"; -is sprintf($ts2char->(), 'foo'), +is sprintf($ts2char->($pg), 'foo'), q{to_char(foo AT TIME ZONE 'UTC', '"year":YYYY:"month":MM:"day":DD:"hour":HH24:"minute":MI:"second":SS:"time_zone":"UTC"')}, '_ts2char_format should work'; @@ -269,6 +290,187 @@ for my $spec ( } $mock_sqitch->unmock('probe'); +############################################################################## +# Test table error and listagg methods. +DBI: { + local *DBI::state; + ok !$pg->_no_table_error, 'Should have no table error'; + ok !$pg->_no_column_error, 'Should have no column error'; + + $DBI::state = '42703'; + ok !$pg->_no_table_error, 'Should again have no table error'; + ok $pg->_no_column_error, 'Should now have no column error'; + + # Need to mock DBH for table errors. + my $dbh = DBI->connect('dbi:Mem:', undef, undef, {}); + my $mock_engine = Test::MockModule->new($CLASS); + $mock_engine->mock(dbh => $dbh); + my $mock_dbd = Test::MockModule->new(ref $dbh, no_auto => 1); + $mock_dbd->mock(quote => sub { qq{'$_[1]'} }); + my @done; + $mock_dbd->mock(do => sub { shift; @done = @_ }); + + # Should just work when on 8.4. + $DBI::state = '42P01'; + $dbh->{pg_server_version} = 80400; + ok $pg->_no_table_error, 'Should now have table error'; + ok !$pg->_no_column_error, 'Still should have no column error'; + is_deeply \@done, [], 'No SQL should have been run'; + + # On 9.0 and later, we should send warnings to the log. + $dbh->{pg_server_version} = 90000; + ok $pg->_no_table_error, 'Should again have table error'; + ok !$pg->_no_column_error, 'Still should have no column error'; + is_deeply \@done, [sprintf q{DO $$ + BEGIN + SET LOCAL client_min_messages = 'ERROR'; + RAISE WARNING USING ERRCODE = 'undefined_table', MESSAGE = %s, DETAIL = %s; + END; + $$}, map { "'$_'" } + __ 'Sqitch registry not initialized', + __ 'Because the "changes" table does not exist, Sqitch will now initialize the database to create its registry tables.', + ], 'Should have sent an error to the log'; + + # Test _listagg_format. + $dbh->{pg_server_version} = 110000; + is $pg->_listagg_format, q{array_remove(array_agg(%1$s ORDER BY %1$s), NULL)}, + 'Should use array_remove and ORDER BY in listagg_format on v11'; + + $dbh->{pg_server_version} = 90300; + is $pg->_listagg_format, q{array_remove(array_agg(%1$s ORDER BY %1$s), NULL)}, + 'Should use array_remove and ORDER BY in listagg_format on v9.3'; + + $dbh->{pg_server_version} = 90200; + is $pg->_listagg_format, + q{ARRAY(SELECT * FROM UNNEST( array_agg(%1$s ORDER BY %1$s) ) a WHERE a IS NOT NULL)}, + 'Should use ORDER BY in listagg_format on v9.2'; + + $dbh->{pg_server_version} = 80400; + is $pg->_listagg_format, + q{ARRAY(SELECT * FROM UNNEST( array_agg(%1$s ORDER BY %1$s) ) a WHERE a IS NOT NULL)}, + 'Should use ORDER BY in listagg_format on v8.4'; + + $dbh->{pg_server_version} = 80300; + is $pg->_listagg_format, + q{ARRAY(SELECT * FROM UNNEST( array_agg(%s) ) a WHERE a IS NOT NULL)}, + 'Should not use ORDER BY in listagg_format on v8.3'; +} + +############################################################################## +# Test _run_registry_file. +RUNREG: { + # Mock I/O used by _run_registry_file. + my $mock_engine = Test::MockModule->new($CLASS); + my (@probed, @prob_ret); + $mock_engine->mock(_probe => sub { + shift; + push @probed, \@_; + shift @prob_ret; + }); + my $psql_maj; + $mock_engine->mock(_psql_major_version => sub { $psql_maj }); + my @ran; + $mock_engine->mock(_run => sub { shift; push @ran, \@_ }); + + # Mock up the database handle. + my $dbh = DBI->connect('dbi:Mem:', undef, undef, {}); + $mock_engine->mock(dbh => $dbh ); + my $mock_dbd = Test::MockModule->new(ref $dbh, no_auto => 1); + my @done; + $mock_dbd->mock(do => sub { shift; push @done, \@_; 1 }); + my @sra_args; + $mock_dbd->mock(selectrow_array => sub { + shift; + push @sra_args, [@_]; + return (qq{"$_[-1]"}); + }); + + # Mock File::Temp so we hang on to the file. + my $mock_ft = Test::MockModule->new('File::Temp'); + my $tmp_fh; + my $ft_new; + $mock_ft->mock(new => sub { $tmp_fh = 'File::Temp'->$ft_new() }); + $ft_new = $mock_ft->original('new'); + + # Find the SQL file. + my $ddl = file($INC{'App/Sqitch/Engine/pg.pm'})->dir->file('pg.sql'); + + # The XC query. + my $xc_query = q{ + SELECT count(*) + FROM pg_catalog.pg_proc p + JOIN pg_catalog.pg_namespace n ON p.pronamespace = n.oid + WHERE nspname = 'pg_catalog' + AND proname = 'pgxc_version'; + }; + + # Start with a recent version and no XC. + $psql_maj = 11; + @prob_ret = (110000, 0); + my $registry = $pg->registry; + ok $pg->_run_registry_file($ddl), 'Run the registry file'; + is_deeply \@probed, [ + ['-c', 'SHOW server_version_num'], + ['-c', $xc_query], + ], 'Should have fetched the server version and checked for XC'; + is_deeply \@ran, [[ + '--file' => $ddl, + '--set' => "registry=$registry", + '--set' => "tableopts=", + ]], 'Shoud have deployed the original SQL file'; + is_deeply \@done, [['SET search_path = ?', undef, $registry]], + 'The registry should have been added to the search path'; + is_deeply \@sra_args, [], 'Should not have have called selectrow_array'; + is $tmp_fh, undef, 'Should have no temp file handle'; + + # Reset and try Postgres 9.2 server + @probed = @ran = @done = (); + $psql_maj = 11; + @prob_ret = (90200, 1); + ok $pg->_run_registry_file($ddl), 'Run the registry file again'; + is_deeply \@probed, [ + ['-c', 'SHOW server_version_num'], + ['-c', $xc_query], + ], 'Should have again fetched the server version and checked for XC'; + isnt $tmp_fh, undef, 'Should now have a temp file handle'; + is_deeply \@ran, [[ + '--file' => $tmp_fh, + '--set' => "tableopts= DISTRIBUTE BY REPLICATION", + ]], 'Shoud have deployed the temp SQL file'; + is_deeply \@sra_args, [], 'Still hould not have have called selectrow_array'; + is_deeply \@done, [['SET search_path = ?', undef, $registry]], + 'The registry should have been added to the search path again'; + + # Make sure the file was changed to remove SCHEMA IF NOT EXISTS. + file_contents_like $tmp_fh, qr/\QCREATE SCHEMA :"registry";/, + 'Should have removed IF NOT EXISTS from CREATE SCHEMA'; + + # Reset and try with Server 11 and psql 8.x. + @probed = @ran = @done = (); + $psql_maj = 8; + $tmp_fh = undef; + @prob_ret = (110000, 0); + ok $pg->_run_registry_file($ddl), 'Run the registry file again'; + is_deeply \@probed, [ + ['-c', 'SHOW server_version_num'], + ['-c', $xc_query], + ], 'Should have again fetched the server version and checked for XC'; + isnt $tmp_fh, undef, 'Should now have a temp file handle'; + is_deeply \@ran, [[ + '--file' => $tmp_fh, + '--set' => "tableopts=", + ]], 'Shoud have deployed the temp SQL file'; + is_deeply \@sra_args, [['SELECT quote_ident(?)', undef, $registry]], + 'Should have have called quote_ident via selectrow_array'; + is_deeply \@done, [['SET search_path = ?', undef, qq{"$registry"}]], + 'The registry should have been added to the search path again'; + + file_contents_like $tmp_fh, qr/\QCREATE SCHEMA IF NOT EXISTS "$registry";/, + 'Should not have removed IF NOT EXISTS from CREATE SCHEMA'; + file_contents_unlike $tmp_fh, qr/:"registry"/, + 'Should have removed the :"registry" variable'; +} + ############################################################################## # Can we do live tests? $config->replace('core.engine' => 'pg'); @@ -345,10 +547,14 @@ DBIEngineTest->run( is $dbh->selectcol_arrayref('SELECT current_schema')->[0], $reg2, 'The Sqitch schema should be the current schema'; }, - lock_sql => { - is_locked => q{SELECT 1 FROM pg_locks WHERE locktype = 'advisory' AND objid = 75474063 AND objsubid = 1}, - try_lock => 'SELECT pg_try_advisory_lock(75474063)', - free_lock => 'SELECT pg_advisory_unlock_all()', + lock_sql => sub { + my $engine = shift; + return { + is_locked => q{SELECT 1 FROM pg_locks WHERE locktype = 'advisory' AND objid = 75474063 AND objsubid = 1}, + try_lock => 'SELECT pg_try_advisory_lock(75474063)', + free_lock => 'SELECT pg_advisory_unlock_all()', + } if $engine->_provider ne 'yugabyte'; + return undef; }, ); diff --git a/t/plan_cmd.t b/t/plan_cmd.t index bf33393ba..afdcd2fd4 100644 --- a/t/plan_cmd.t +++ b/t/plan_cmd.t @@ -3,8 +3,8 @@ use strict; use warnings; use utf8; -use Test::More tests => 229; -#use Test::More 'no_plan'; +use Test::More tests => 232; +# use Test::More 'no_plan'; use App::Sqitch; use Locale::TextDomain qw(App-Sqitch); use Test::NoWarnings; @@ -25,7 +25,7 @@ require_ok $CLASS; my $config = TestConfig->new( 'core.engine' => 'sqlite', 'core.top_dir' => dir('test-plan_cmd')->stringify, - 'core.plan_file' => file(qw(t sql sqitch.plan))->stringify, + 'core.plan_file' => file(qw(t plans dependencies.plan))->stringify, ); ok my $sqitch = App::Sqitch->new(config => $config), 'Load a sqitch sqitch object'; @@ -621,6 +621,32 @@ is_deeply +MockOutput->get_page, [ [ $cmd->formatter->format( $cmd->format, $fmt_params2 ) ], ], 'Both events should have been paged without headers'; +# Now try raw format of all the changes. +my $cfg = $CLASS->configure( $config, { format => 'raw' } ); +ok $cmd = $CLASS->new( sqitch => $sqitch, %{ $cfg } ), + 'Create command with raw format'; +push @changes => $plan->changes; + +ok $cmd->execute, 'Execute plan with all changes'; +is_deeply +MockOutput->get_page, [ + ['# ', __x 'Project: {project}', project => $plan->project ], + ['# ', __x 'File: {file}', file => $plan->file ], + [''], + map { [ $cmd->formatter->format( $cmd->format, { + event => $_->is_deploy ? 'deploy' : 'revert', + project => $_->project, + change_id => $_->id, + change => $_->name, + note => $_->note, + tags => [ map { $_->format_name } $_->tags ], + requires => [ map { $_->as_string } $_->requires ], + conflicts => [ map { $_->as_string } $_->conflicts ], + planned_at => $_->timestamp, + planner_name => $_->planner_name, + planner_email => $_->planner_email, + } ) ] } $plan->changes +], 'Should have paged all changes'; + # Make sure we catch bad format codes. isa_ok $cmd = $CLASS->new( sqitch => $sqitch, @@ -646,7 +672,7 @@ $mock_cmd->mock(parse_args => sub { }); $orig_parse = $mock_cmd->original('parse_args'); -# Try specifying an unkonwn target. +# Try specifying an unknown target. ok $cmd = $CLASS->new( sqitch => $sqitch, target => 'foo'), 'Create plan command with unknown target option'; throws_ok { $cmd->execute } 'App::Sqitch::X', diff --git a/t/rework.t b/t/rework.t index d252f3701..a1fa75365 100644 --- a/t/rework.t +++ b/t/rework.t @@ -227,6 +227,9 @@ is_deeply +MockOutput->get_info, [ [" * $verify_file"], ], 'And the info message should suggest editing the old files'; is_deeply +MockOutput->get_debug, [ + [' ', __x 'Created {file}', file => dir qw(test-rework deploy) ], + [' ', __x 'Created {file}', file => dir qw(test-rework revert) ], + [' ', __x 'Created {file}', file => dir qw(test-rework verify) ], [__x( 'Copied {src} to {dest}', dest => $deploy_file2, diff --git a/t/snowflake.t b/t/snowflake.t index 7d1a20c31..a0984ce84 100644 --- a/t/snowflake.t +++ b/t/snowflake.t @@ -336,7 +336,8 @@ is_deeply [$snow->snowsql], [qw( ############################################################################## # Test SQL helpers. -is $snow->_listagg_format, q{listagg(%s, ' ')}, 'Should have _listagg_format'; +is $snow->_listagg_format, q{listagg(%1$s, ' ') WITHIN GROUP (ORDER BY %1$s)}, + 'Should have _listagg_format'; is $snow->_ts_default, 'current_timestamp', 'Should have _ts_default'; is $snow->_regex_op, 'REGEXP', 'Should have _regex_op'; is $snow->_simple_from, ' FROM dual', 'Should have _simple_from'; @@ -373,6 +374,15 @@ is_deeply [$snow->_regex_expr('corn', 'Obama$')], ["regexp_substr(corn, ?) IS NOT NULL", 'Obama$'], 'Should use regexp_substr IS NOT NULL for regex expr'; +############################################################################## +# Test unexpeted datbase error in _cid(). +$mock_snow->mock(dbh => sub { die 'OW' }); +throws_ok { $snow->initialized } qr/OW/, + 'initialized() should rethrow unexpected DB error'; +throws_ok { $snow->_cid } qr/OW/, + '_cid should rethrow unexpected DB error'; +$mock_snow->unmock('dbh'); + ############################################################################## # Test _run(), _capture() _spool(), and _probe(). $config->replace('core.engine' => 'snowflake'); diff --git a/t/sqitch.conf b/t/sqitch.conf index ca5c4f9ad..435d61bc3 100644 --- a/t/sqitch.conf +++ b/t/sqitch.conf @@ -19,6 +19,7 @@ dest_dir = _build/sql [foo "BAR"] baz = hello + yep [guess "Yes.No"] red = true Calico = false diff --git a/t/sqlite.t b/t/sqlite.t index f5193004a..ce18fc45b 100644 --- a/t/sqlite.t +++ b/t/sqlite.t @@ -145,20 +145,20 @@ is $sqlite->registry_destination, $sqlite->registry_uri->as_string, $config->update( 'engine.sqlite.registry' => 'registry', 'engine.sqlite.target' => 'noext', - 'target.noext.uri' => 'db:sqlite:/path/to/sqitch.db', + 'target.noext.uri' => 'db:sqlite://x:foo@/path/to/sqitch.db', ); $target = ref($target)->new( sqitch => $sqitch ); ok $sqlite = $CLASS->new(sqitch => $sqitch, target => $target), 'Create another sqlite'; -is $sqlite->uri->as_string, 'db:sqlite:/path/to/sqitch.db', +is $sqlite->uri->as_string, 'db:sqlite://x:foo@/path/to/sqitch.db', 'dbname should fall back on config with no extension'; is $sqlite->target, $target, 'Target should be as specified'; is $sqlite->destination, 'noext', 'Destination should be configured target name'; -is $sqlite->registry_uri->as_string, 'db:sqlite:/path/to/registry.db', +is $sqlite->registry_uri->as_string, 'db:sqlite://x:foo@/path/to/registry.db', 'registry_uri should fall back on config wth extension'; -is $sqlite->registry_destination, $sqlite->registry_uri->as_string, - 'Registry target should be configured registry_uri stringified'; +is $sqlite->registry_destination, 'db:sqlite://x:@/path/to/registry.db', + 'Registry target should be configured registry_uri without password'; # Try a registry with an absolute path. $config->update( @@ -329,6 +329,22 @@ $mock_sqitch->mock(capture => sub { return ( "\n",$sqlite_version) }); # Un-mock for live tests below $mock_sqitch->unmock_all; +############################################################################## +# Test error checking functions. +DBI: { + local *DBI::errstr; + ok !$sqlite->_no_table_error, 'Should have no table error'; + ok !$sqlite->_no_column_error, 'Should have no column error'; + + $DBI::errstr = 'no such table: xyz'; + ok $sqlite->_no_table_error, 'Should now have table error'; + ok !$sqlite->_no_column_error, 'Still should have no column error'; + + $DBI::errstr = 'no such column: xyz'; + ok !$sqlite->_no_table_error, 'Should again have no table error'; + ok $sqlite->_no_column_error, 'Should now have no column error'; +} + ############################################################################## my $alt_db = $db_name->dir->file("sqitchtest$id.db"); my ($reg1, $reg2) = map { $_ . $id } qw(sqitch sqitchtest); diff --git a/t/vertica.t b/t/vertica.t index 01b01070e..4f56cdb82 100644 --- a/t/vertica.t +++ b/t/vertica.t @@ -151,8 +151,8 @@ is_deeply [$vta->vsql], [ ], 'vsql command should be configured from URI config'; ############################################################################## -# Test _run(), _capture(), and _spool(). -can_ok $vta, qw(_run _capture _spool); +# Test _run(), _capture(), _spool(), and _prob() +can_ok $vta, qw(_run _capture _spool _probe); my $mock_sqitch = Test::MockModule->new('App::Sqitch'); my (@run, $exp_pass); $mock_sqitch->mock(run => sub { @@ -190,6 +190,18 @@ $mock_sqitch->mock(spool => sub { } }); +my @probe; +$mock_sqitch->mock(probe => sub { + local $Test::Builder::Level = $Test::Builder::Level + 2; + shift; + @probe = @_; + if (defined $exp_pass) { + is $ENV{VSQL_PASSWORD}, $exp_pass, qq{VSQL_PASSWORD should be "$exp_pass"}; + } else { + ok !exists $ENV{VSQL_PASSWORD}, 'VSQL_PASSWORD should not exist'; + } +}); + $exp_pass = 's3cr3t'; $target->uri->password($exp_pass); ok $vta->_run(qw(foo bar baz)), 'Call _run'; @@ -204,6 +216,9 @@ ok $vta->_capture(qw(foo bar baz)), 'Call _capture'; is_deeply \@capture, [$vta->vsql, qw(foo bar baz)], 'Command should be passed to capture()'; +ok $vta->_probe(qw(hi there)), 'Call _probe'; +is_deeply \@probe, [$vta->vsql, qw(hi there)]; + # Without password. $target = App::Sqitch::Target->new( sqitch => $sqitch ); ok $vta = $CLASS->new(sqitch => $sqitch, target => $target), @@ -221,6 +236,9 @@ ok $vta->_capture(qw(foo bar baz)), 'Call _capture again'; is_deeply \@capture, [$vta->vsql, qw(foo bar baz)], 'Command should be passed to capture() again'; +ok $vta->_probe(qw(go there)), 'Call _probe'; +is_deeply \@probe, [$vta->vsql, qw(go there)]; + ############################################################################## # Test file and handle running. ok $vta->run_file('foo/bar.sql'), 'Run foo/bar.sql'; @@ -244,7 +262,7 @@ is_deeply \@run, [$vta->vsql, '--file', 'foo/bar.sql'], $mock_sqitch->unmock_all; ############################################################################## -# Test DateTime formatting stuff. +# Test DateTime formatting and other database stuff. ok my $ts2char = $CLASS->can('_ts2char_format'), "$CLASS->can('_ts2char_format')"; is sprintf($ts2char->(), 'foo'), q{to_char(foo AT TIME ZONE 'UTC', '"year":YYYY:"month":MM:"day":DD:"hour":HH24:"minute":MI:"second":SS:"time_zone":"UTC"')}, @@ -261,6 +279,41 @@ is $dt->hour, 15, 'DateTime hour should be set'; is $dt->minute, 7, 'DateTime minute should be set'; is $dt->second, 1, 'DateTime second should be set'; is $dt->time_zone->name, 'UTC', 'DateTime TZ should be set'; +is $vta->_listagg_format, undef, 'Should have no listagg format'; + +############################################################################## +# Test table error methods. +DBI: { + local *DBI::state; + ok !$vta->_no_table_error, 'Should have no table error'; + ok !$vta->_no_column_error, 'Should have no column error'; + + $DBI::state = '42V01'; + ok $vta->_no_table_error, 'Should now have table error'; + ok !$vta->_no_column_error, 'Still should have no column error'; + + $DBI::state = '42703'; + ok !$vta->_no_table_error, 'Should again have no table error'; + ok $vta->_no_column_error, 'Should now have no column error'; +} + +############################################################################## +# Test current state error handling. +CS: { + my $mock_engine = Test::MockModule->new($CLASS); + $mock_engine->mock(_select_state => sub { die 'OW' }); + throws_ok { $vta->current_state } qr/OW/, + "current_state should propagate an error when it's not a column error"; +} + +############################################################################## +# Test _cid error handling. +CID: { + my $mock_engine = Test::MockModule->new($CLASS); + $mock_engine->mock(dbh => sub { die 'OH NO' }); + throws_ok { $vta->_cid } qr/OH NO/, + "_cid should propagate an error when it's not a table or column error"; +} ############################################################################## # Can we do live tests? @@ -285,17 +338,27 @@ $uri = URI->new( $ENV{VSQL_URI} || 'db:vertica://dbadmin:password@localhost/dbadmin' ); -my $err = try { - $vta->use_driver; - $dbh = DBI->connect($uri->dbi_dsn, $uri->user, $uri->password, { - PrintError => 0, - RaiseError => 1, - AutoCommit => 1, - }); - undef; -} catch { - eval { $_->message } || $_; -}; + +# Try to connect. +my $err; +for my $i (1..30) { + $err = try { + $vta->use_driver; + $dbh = DBI->connect($uri->dbi_dsn, $uri->user, $uri->password, { + PrintError => 0, + RaiseError => 1, + AutoCommit => 1, + }); + undef; + } catch { + eval { $_->message } || $_; + }; + # Sleep if it failed but Vertica is still starting up. + # SQL-57V03: `failed: FATAL 4149: Node startup/recovery in progress. Not yet ready to accept connections` + # SQL-08001: `failed: [Vertica][DSI] An error occurred while attempting to retrieve the error message for key 'VConnectFailed' and component ID 101: Could not open error message files` + last unless $err && (($DBI::state || '') eq '57V03' || $err =~ /VConnectFailed/); + sleep 1 if $i < 30; +} DBIEngineTest->run( class => $CLASS, diff --git a/t/win32.t b/t/win32.t new file mode 100644 index 000000000..38c192b99 --- /dev/null +++ b/t/win32.t @@ -0,0 +1,17 @@ +#!/usr/bin/perl -w + +# local *main::^O; +BEGIN { + $^O = 'MSWin32'; +} + +use strict; +use warnings; +use Test::More tests => 2; +use Try::Tiny; +use App::Sqitch::ItemFormatter; + +is $^O, 'MSWin32', 'Should have "MSWin32"'; +is App::Sqitch::ItemFormatter::CAN_OUTPUT_COLOR, + try { require Win32::Console::ANSI }, + 'CAN_OUTPUT_COLOR should be set properly'; diff --git a/xt/release.md b/xt/release.md index 617046a3e..bf68dd4b4 100644 --- a/xt/release.md +++ b/xt/release.md @@ -18,7 +18,7 @@ First, update the sources so that everything is up-to-date. ``` sh cpan Dist::Zilla - dzil authordeps --missing | cpanm + dzil authordeps --missing | cpanm --notest ``` * Update the translation dictionaries: @@ -26,18 +26,21 @@ First, update the sources so that everything is up-to-date. ``` sh dzil msg-scan perl -i -pe 's/\QSOME DESCRIPTIVE TITLE./Sqitch Localization Messages/' po/App-Sqitch.pot - perl -i -pe 's/\Q(C) YEAR/(c) 2012-2021/' po/App-Sqitch.pot + perl -i -pe "s/\Q(C) YEAR/(c) 2012-$(date +%Y)/" po/App-Sqitch.pot dzil msg-merge + git add po git commit -am 'Update translation dictionaries' ``` * Proofread Changes and fix any spelling or grammatical errors, and edit - descriptions to minimize confusion. + descriptions to minimize confusion. Consider whether the changes constitute + a major, minor, or patch update, and increment the appropriate [semver] + parts (`major.minor.patch`) accordingly. * Update copyright dates if a year has turned over since the last release: ``` sh - grep -ril copyright . | xargs perl -i -pe 's/-2021/-2022/g' + grep -ril copyright . | xargs perl -i -pe "s/-2022/-$(date +%Y)/g" ``` * Make a build and run `xt/dependency_report`: @@ -80,6 +83,7 @@ to get it out there! * Merge `develop` into the `main` branch: ``` sh + git checkout main git merge --no-ff -m "Merge develop for v$VERSION" develop git push ``` @@ -129,28 +133,32 @@ then make the updates. perl -i -pe "s/^(VERSION)=.+/\$1=$VERSION/" build ``` -* Edit the `README.md` to add the new version to the list of tags. It should - also be listed as using the `latest` tag. - * Commit and push the changes: ``` sh - git commit -am "Upgrade Sqitch to v$VERSION' + git commit -am "Upgrade Sqitch to v$VERSION" git push ``` -* Watch the [CI/CD action] to make sure the build finishes and publishes the new - Docker image [on Docker Hub]. +* Watch the [CI/CD action] to make sure the build finishes successfully. Once + it does, tag the commit and push it, appending an extra `.0`: + + ``` sh + git tag -sm "Tag v$VERSION.0" v$VERSION.0 + git push --tags + ``` -* Test the new version with Docker. This command should should show a usage - statement for the new version: +* Watch the [CI/CD action] again, to be sure it publishes the new image [on + Docker Hub]. Then test the new version with Docker. This command should + should show a usage statement for the new version: ``` sh docker run -it --rm sqitch/sqitch:v$VERSION ``` * Log into Docker Hub and update the description of the [repository page] with - the new release tag. Find the credentials in the shared 1Password vault. + the new release tag. It should also be listed as using the `latest` tag. + Find the Docker Hub credentials in the shared 1Password vault. Homebrew -------- @@ -173,8 +181,14 @@ Update the Sqitch Homebrew tap with the new version. ``` sh git commit -am "Upgrade to v$VERSION" - git tag v$VERSION -sm "Tag v$VERSION" git push + ``` + +* Watch the [CI Action] to make sure the build completes successfully, and fix + any issues that arise. Once tests pass, tag the release: + + ``` sh + git tag v$VERSION.0 -sm "Tag v$VERSION.0" git push --tags ``` @@ -214,6 +228,7 @@ Also add a line for the new version (without the pre-release part) to the top of the `Changes` file. Then commit and push the changes and you're done! Time to start work on the next release. Good luck! + [semver]: https://semver.org/ [workflow actions]: https://github.com/sqitchers/sqitch/actions [Release action]: https://github.com/sqitchers/sqitch/actions/workflows/release.yml [GitHub release]: https://github.com/sqitchers/sqitch/releases @@ -225,3 +240,4 @@ start work on the next release. Good luck! [on Docker Hub]: https://hub.docker.com/r/sqitch/sqitch [repository page]: https://hub.docker.com/repository/docker/sqitch/sqitch [homebrew-sqitch]: https://github.com/sqitchers/homebrew-sqitch + [CI Action]: https://github.com/sqitchers/homebrew-sqitch/actions/workflows/ci.yml diff --git a/xt/release/pod-spelling.t b/xt/release/pod-spelling.t index 695d23f29..da0904469 100644 --- a/xt/release/pod-spelling.t +++ b/xt/release/pod-spelling.t @@ -13,6 +13,7 @@ Blockchain blog change's CLDR +CockroachDB command's committer committers @@ -120,4 +121,6 @@ VirtualBox VM XC yay -Yay \ No newline at end of file +Yay +Yugabyte +YugabyteDB