diff --git a/.gitattributes b/.gitattributes index e2b21a8d7..c5e9f777f 100644 --- a/.gitattributes +++ b/.gitattributes @@ -21,15 +21,15 @@ *.gif binary *.ttf binary -# Ignore some meta files when creating an archive of this repository +# Ignore some files when creating an archive of this repository /.github export-ignore /.editorconfig export-ignore /.gitattributes export-ignore /.gitignore export-ignore /phpunit.xml.dist export-ignore +/docker export-ignore /docs export-ignore # Avoid merge conflicts in CHANGELOG # https://about.gitlab.com/2015/02/10/gitlab-reduced-merge-conflicts-by-90-percent-with-changelog-placeholders/ /CHANGELOG.md merge=union - diff --git a/.gitignore b/.gitignore index c942fd526..ddce887d8 100644 --- a/.gitignore +++ b/.gitignore @@ -44,3 +44,6 @@ phpunit.phar #codeception /tests/_output c3.php + +# Docker +docker/docker-compose.override.yml diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..814e74e0a --- /dev/null +++ b/Makefile @@ -0,0 +1,78 @@ +.PHONY: help +help: ## Show the list of available commands with description. + @awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST) +.DEFAULT_GOAL := help + +build: ## Build services. + docker compose -f docker/docker-compose.yml -f docker/docker-compose.override.yml build +up: ## Start services. + docker compose -f docker/docker-compose.yml -f docker/docker-compose.override.yml up -d --remove-orphans +build-up: # Build and start services. + docker compose -f docker/docker-compose.yml -f docker/docker-compose.override.yml up -d --remove-orphans --build +ps: ## List running services + docker compose -f docker/docker-compose.yml -f docker/docker-compose.override.yml ps +stop: ## Stop running services. + docker compose -f docker/docker-compose.yml -f docker/docker-compose.override.yml stop +down: ## Stop running services and remove all services (not defined services, containers, networks, volumes, images). + docker compose -f docker/docker-compose.yml -f docker/docker-compose.override.yml down \ + --remove-orphans \ + --volumes \ + --rmi all + +run: ## Run arbitrary command. + docker compose -f docker/docker-compose.yml -f docker/docker-compose.override.yml run \ + --rm \ + --entrypoint $(CMD) \ + php + +shell: CMD="bash" ## Open interactive shell. +shell: run + +test-all: test-base \ + test-driver-all ## Run all available tests. +test-driver-all: test-driver-sqlite \ + test-driver-mysql \ + test-driver-mariadb \ + test-driver-pgsql \ + test-driver-mssql \ + test-driver-oracle ## Run tests for all drivers. +test-base: testsuite-Db ## Run tests for base db package only. +test-driver-sqlite: testsuite-Sqlite ## Run tests for SQLite driver only. +test-driver-mysql: testsuite-Mysql ## Run tests for MySQL driver only (using MySQL database). +test-driver-mariadb: ## Run tests for MySQL driver only (using MariaDB database). + docker compose -f docker/docker-compose.yml -f docker/docker-compose.override.yml run \ + --rm \ + --entrypoint "vendor/bin/phpunit --testsuite Mysql $(RUN_ARGS)" \ + -e YII_MYSQL_TYPE=mariadb \ + php +test-driver-pgsql: testsuite-Pgsql ## Run tests for PostgreSQL driver only. +test-driver-mssql: testsuite-Mssql ## Run tests for Microsoft SQL Server driver only. +test-driver-oracle: ## Run tests for Oracle driver only. + docker compose -f docker/docker-compose.yml -f docker/docker-compose.override.yml run \ + --rm \ + --entrypoint "bash -c -l 'vendor/bin/phpunit --testsuite Oracle $(RUN_ARGS)'" \ + php + +testsuite-%: + docker compose -f docker/docker-compose.yml -f docker/docker-compose.override.yml run \ + --rm \ + --entrypoint "vendor/bin/phpunit --testsuite $(subst testsuite-,,$@) $(RUN_ARGS)" \ + php + +mutation: CMD="\ +vendor/bin/roave-infection-static-analysis-plugin \ +--threads=2 \ +--min-msi=0 \ +--min-covered-msi=100 \ +--ignore-msi-with-no-mutations \ +--only-covered" ## Run mutation tests using Infection. +mutation: run + +static-analysis: CMD="vendor/bin/psalm --no-cache" ## Run static analysis using Psalm. +static-analysis: run + +rector: CMD="vendor/bin/rector" ## Check code style using Rector. +rector: run + +composer-require-checker: CMD="vendor/bin/composer-require-checker" ## Check dependencies using Composer Require Checker. +composer-require-checker: run diff --git a/composer.json b/composer.json index d0e6495f2..bf75d7cc3 100644 --- a/composer.json +++ b/composer.json @@ -61,6 +61,13 @@ }, "autoload-dev": { "psr-4": { + "Yiisoft\\ActiveRecord\\": "vendor/yiisoft/active-record/src", + "Yiisoft\\Db\\Migration\\": "vendor/yiisoft/db-migration/src", + "Yiisoft\\Db\\Mssql\\": "vendor/yiisoft/db-mssql/src", + "Yiisoft\\Db\\Mysql\\": "vendor/yiisoft/db-mysql/src", + "Yiisoft\\Db\\Oracle\\": "vendor/yiisoft/db-oracle/src", + "Yiisoft\\Db\\Pgsql\\": "vendor/yiisoft/db-pgsql/src", + "Yiisoft\\Db\\Sqlite\\": "vendor/yiisoft/db-sqlite/src", "Yiisoft\\Db\\Tests\\": "tests", "Yiisoft\\ActiveRecord\\Tests\\": "vendor/yiisoft/active-record/tests", "Yiisoft\\Db\\Mssql\\Tests\\": "vendor/yiisoft/db-mssql/tests", diff --git a/docker/Dockerfile b/docker/Dockerfile new file mode 100644 index 000000000..1d2ca6e18 --- /dev/null +++ b/docker/Dockerfile @@ -0,0 +1,88 @@ +FROM composer/composer:latest-bin AS composer + +FROM php:8.3-cli + +# System packages + +RUN apt-get update && apt-get install -y \ + unzip \ + # PostgreSQL + libpq-dev \ + # MSSQL + apt-transport-https \ + gnupg2 \ + libpng-dev \ + # Oracle + libaio1 && \ + rm -rf /var/lib/apt/lists/* + +# MSSQL dependencies + +ENV ACCEPT_EULA=Y + +# Install prerequisites for the sqlsrv and pdo_sqlsrv PHP extensions. +# Some packages are pinned with lower priority to prevent build issues due to package conflicts. +# Link: https://github.com/microsoft/linux-package-repositories/issues/39 +RUN curl https://packages.microsoft.com/keys/microsoft.asc | apt-key add - \ + && curl https://packages.microsoft.com/config/debian/11/prod.list > /etc/apt/sources.list.d/mssql-release.list \ + && echo "Package: unixodbc\nPin: origin \"packages.microsoft.com\"\nPin-Priority: 100\n" >> /etc/apt/preferences.d/microsoft \ + && echo "Package: unixodbc-dev\nPin: origin \"packages.microsoft.com\"\nPin-Priority: 100\n" >> /etc/apt/preferences.d/microsoft \ + && echo "Package: libodbc1:amd64\nPin: origin \"packages.microsoft.com\"\nPin-Priority: 100\n" >> /etc/apt/preferences.d/microsoft \ + && echo "Package: odbcinst\nPin: origin \"packages.microsoft.com\"\nPin-Priority: 100\n" >> /etc/apt/preferences.d/microsoft \ + && echo "Package: odbcinst1debian2:amd64\nPin: origin \"packages.microsoft.com\"\nPin-Priority: 100\n" >> /etc/apt/preferences.d/microsoft \ + && apt-get update \ + && apt-get install -y msodbcsql18 mssql-tools18 unixodbc-dev \ + && rm -rf /var/lib/apt/lists/* + +# Oracle dependencies + +RUN cd /tmp && curl -L https://download.oracle.com/otn_software/linux/instantclient/2350000/instantclient-basic-linux.x64-23.5.0.24.07.zip -O +RUN cd /tmp && curl -L https://download.oracle.com/otn_software/linux/instantclient/2350000/instantclient-sdk-linux.x64-23.5.0.24.07.zip -O +RUN cd /tmp && curl -L https://download.oracle.com/otn_software/linux/instantclient/2350000/instantclient-sqlplus-linux.x64-23.5.0.24.07.zip -O + +RUN unzip /tmp/instantclient-basic-linux.x64-23.5.0.24.07.zip -d /usr/local/ +RUN unzip -o /tmp/instantclient-sdk-linux.x64-23.5.0.24.07.zip -d /usr/local/ +RUN unzip -o /tmp/instantclient-sqlplus-linux.x64-23.5.0.24.07.zip -d /usr/local/ + +RUN ln -s /usr/local/instantclient_23_5 /usr/local/instantclient +RUN ln -s /usr/local/instantclient/lib* /usr/lib +RUN ln -s /usr/local/instantclient/sqlplus /usr/bin/sqlplus + +RUN echo 'export LD_LIBRARY_PATH="/usr/local/instantclient"' >> /root/.bashrc +RUN echo 'umask 002' >> /root/.bashrc + +# PHP extensions + +RUN docker-php-ext-install \ + pdo_mysql \ + pdo_pgsql \ + # For Psalm, to make use of JIT for a 20%+ performance boost. + opcache + +RUN pecl install sqlsrv +RUN printf "; priority=20\nextension=sqlsrv.so\n" > /usr/local/etc/php/conf.d/php-sqlsrv.ini + +RUN pecl install pdo_sqlsrv +RUN printf "; priority=30\nextension=pdo_sqlsrv.so\n" > /usr/local/etc/php/conf.d/php-pdo-sqlsrv.ini + +RUN echo 'instantclient,/usr/local/instantclient' | pecl install oci8 +RUN echo "extension=oci8.so" > /usr/local/etc/php/conf.d/php-oci8.ini + +RUN echo 'instantclient,/usr/local/instantclient' | pecl install pdo_oci +RUN echo "extension=pdo_oci.so" > /usr/local/etc/php/conf.d/php-pdo-oci.ini + +# For code coverage (mutation testing) +RUN pecl install pcov && docker-php-ext-enable pcov + +# Composer + +COPY --from=composer /composer /usr/bin/composer + +# Code + +COPY . /code +WORKDIR /code + +# PHP packages + +RUN COMPOSER_ALLOW_SUPERUSER=1 composer install diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml new file mode 100644 index 000000000..b51daf72c --- /dev/null +++ b/docker/docker-compose.yml @@ -0,0 +1,150 @@ +name: yiisoft-db +services: + php: + build: + context: ./.. + dockerfile: ./docker/Dockerfile + volumes: + - ./../src:/code/src + - ./../tests:/code/tests + - ./../composer.json:/code/composer.json + - ./../phpunit.xml.dist:/code/phpunit.xml.dist + environment: + YII_MYSQL_DATABASE: yii + YII_MYSQL_HOST: mysql + YII_MYSQL_PORT: 3306 + YII_MYSQL_USER: root + YII_MYSQL_PASSWORD: root + YII_MYSQL_TYPE: mysql + + YII_MARIADB_DATABASE: yii + YII_MARIADB_HOST: mariadb + YII_MARIADB_PORT: 13306 + YII_MARIADB_USER: root + YII_MARIADB_PASSWORD: root + + YII_PGSQL_DATABASE: yii + YII_PGSQL_HOST: postgres + YII_PGSQL_PORT: 5432 + YII_PGSQL_USER: postgres + YII_PGSQL_PASSWORD: postgres + + YII_MSSQL_DATABASE: tempdb + YII_MSSQL_HOST: mssql + YII_MSSQL_PORT: 1433 + YII_MSSQL_USER: SA + YII_MSSQL_PASSWORD: YourStrong!Passw0rd + + YII_ORACLE_SID: FREE + YII_ORACLE_DATABASE: FREEPDB1 + YII_ORACLE_HOST: oracle + YII_ORACLE_PORT: 1521 + YII_ORACLE_USER: system + YII_ORACLE_PASSWORD: sys_user_password + + ORACLE_PASSWORD: sys_user_password + APP_USER: my_user + APP_USER_PASSWORD: password_i_should_change + command: tail -F anything + depends_on: + mysql: + condition: service_healthy + mariadb: + condition: service_healthy + postgres: + condition: service_healthy + mssql: + condition: service_healthy + oracle: + condition: service_healthy + mysql: + image: mysql:9 + ports: + - "3306:3306" + volumes: + - type: tmpfs + target: /var/lib/mysql + environment: + MYSQL_DATABASE: yii + MYSQL_ROOT_PASSWORD: root + MYSQL_ROOT_HOST: "%" + healthcheck: + test: [ "CMD", "mysqladmin" ,"ping", "-h", "localhost", "-uroot" ] + interval: 5s + timeout: 5s + retries: 20 + mariadb: + image: mariadb:11 + ports: + - "13306:3306" + volumes: + - type: tmpfs + target: /var/lib/mysql + environment: + MYSQL_DATABASE: yii + MYSQL_ROOT_PASSWORD: root + MYSQL_ROOT_HOST: "%" + MYSQL_TCP_PORT: 13306 + healthcheck: + test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"] + start_period: 10s + interval: 10s + timeout: 5s + retries: 30 + postgres: + image: postgres:17 + ports: + - "5432:5432" + volumes: + - type: tmpfs + target: /var/lib/postgresql/data + environment: + POSTGRES_DB: yii + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + healthcheck: + test: [ "CMD-SHELL", "pg_isready -U postgres" ] + interval: 5s + timeout: 5s + retries: 5 + mssql: + image: mcr.microsoft.com/mssql/server:2022-latest + ports: + - "1433:1433" + user: root + volumes: + - mssql-data:/var/opt/mssql/data + - mssql-log:/var/opt/mssql/log + - mssql-secrets:/var/opt/mssql/secrets + environment: + SA_PASSWORD: YourStrong!Passw0rd + ACCEPT_EULA: Y + healthcheck: + test: /opt/mssql-tools18/bin/sqlcmd -S localhost -U sa -P "$${SA_PASSWORD}" -Q "SELECT 1" -b -C -o /dev/null + interval: 10s + timeout: 3s + retries: 100 + start_period: 10s + oracle: + build: + context: oracle + ports: + - "1521:1521" + volumes: + - oracle-data:/opt/oracle/oradata + environment: + ORACLE_PASSWORD: sys_user_password + APP_USER: my_user + APP_USER_PASSWORD: password_i_should_change + healthcheck: + test: ["CMD", "healthcheck.sh"] + interval: 10s + timeout: 5s + retries: 100 + start_period: 5s + start_interval: 5s +volumes: + mssql-data: + mssql-log: + mssql-secrets: + oracle-data: diff --git a/docker/oracle/Dockerfile b/docker/oracle/Dockerfile new file mode 100644 index 000000000..cc7e1498a --- /dev/null +++ b/docker/oracle/Dockerfile @@ -0,0 +1,3 @@ +FROM gvenzl/oracle-free:23 + +RUN chown -R 54321:54321 /opt/oracle/oradata && chmod 0777 /opt/oracle/oradata diff --git a/docs/internals.md b/docs/internals.md index 1d151b86e..11761ee2a 100644 --- a/docs/internals.md +++ b/docs/internals.md @@ -6,86 +6,142 @@ All our packages have github actions by default, so you can test your [contribut > Note: We recommend pull requesting in draft mode until all tests pass. -## Unit testing +## Local development -This package can be tested globally or individually for each DBMS. +Docker is used to ease the local development. -- [MSSQL](https://github.com/yiisoft/db-mssql) -- [MySQL/MariaDB](https://github.com/yiisoft/db-mysql) -- [Oracle](https://github.com/yiisoft/db-oracle) -- [PostgreSQL](https://github.com/yiisoft/db-pgsql) -- [SQLite](https://github.com/yiisoft/db-sqlite) +## Prerequisites: -### Docker images +### Docker Compose -For greater ease it is recommended to use Docker containers for each DBMS, for this you can use the [docker-compose.yml](https://docs.docker.com/compose/compose-file/) file that in the root directory of each package. +Install [Docker](https://docs.docker.com/get-started/get-docker/) with +[Docker Compose](https://docs.docker.com/compose/install/). -- [MSSQL 2022](https://github.com/yiisoft/db-mssql/blob/master/docker-compose.yml) -- [MySQL 8](https://github.com/yiisoft/db-mysql/blob/master/docker-compose.yml) -- [MariaDB 10.11](https://github.com/yiisoft/db-mysql/blob/master/docker-compose-mariadb.yml) -- [Oracle 21](https://github.com/yiisoft/db-oracle/blob/master/docker-compose.yml) -- [PostgreSQL 15](https://github.com/yiisoft/db-pgsql/blob/master/docker-compose.yml) +### make -For running the Docker containers you can use the following command: +make is required for running commands. To get it working on Windows, some of the options are: -```shell -docker compose up -d +- It ships with [Git BASH](https://git-scm.com/downloads/win). +- Installation using [Chocolatey](https://chocolatey.org). [Install](https://chocolatey.org/install) Chocolatey first, +then run `choco install make`. +- It is available within [Cygwin](https://www.cygwin.com/). +- Use [WSL](https://learn.microsoft.com/en-us/windows/wsl/install). + +### Setup + +Clone all repos of drivers' packages: + +- [SQLite](https://github.com/yiisoft/db-sqlite) +- [MySQL](https://github.com/yiisoft/db-mysql) +- [PostgreSQL](https://github.com/yiisoft/db-pgsql) +- [Microsoft SQL Server](https://github.com/yiisoft/db-mssql) +- [Oracle](https://github.com/yiisoft/oracle) + +Create `docker/docker-compose.override.yml` file with this content: + +```yaml +services: + php: + volumes: + - /path/to/packages/db-sqlite:/code/vendor/yiisoft/db-sqlite + - /path/to/packages/db-mysql:/code/vendor/yiisoft/db-mysql + - /path/to/packages/db-pgsql:/code/vendor/yiisoft/db-pgsql + - /path/to/packages/db-mssql:/code/vendor/yiisoft/db-mssql + - /path/to/packages/db-oracle:/code/vendor/yiisoft/db-oracle ``` -### Global testing +Adjust the `/path/to/packages` to the path where packages are installed on your host machine. -The following steps are required to run the tests. +In case of ports' collisions, the mapping and environment variables can also be adjusted here. -1. Run all Docker containers for each DBMS. -2. Install the dependencies of the project with composer. -3. Run the tests. +### Unit testing -```shell -./vendor/bin/phpunit -``` +#### Available commands + +- `make test-all` - run all available tests. +- `make test-base` - run tests for base db package only. +- `make test-driver-all` - run tests for all drivers. +- `make test-driver-sqlite` - run tests for SQLite driver only. +- `make test-driver-mysql` - run tests for MySQL driver only (using MySQL database). +- `make test-driver-mariadb` - run tests for MySQL driver only (using MariaDB database). +- `make test-driver-pgsql` - run tests for PostgreSQL driver only. +- `make test-driver-mssql` - run tests for Microsoft SQL Server driver only. +- `make test-driver-oracle`- run tests for Oracle driver only. -### Individual testing +#### Testing different versions -The following steps are required to run the tests. +Docker Compose services use the following stack: -1. Run the Docker container for the dbms you want to test. -2. Install the dependencies of the project with composer. -3. Run the tests. +- PHP 8.3. +- MySQL 9. +- MariaDB 11. +- PostgreSQL 19. +- Microsoft SQL Server 2022. +- Oracle Free 23. + +Different versions are available in GitHub Actions. Other versions of RDBMS might be added to Docker Compose in the +future. + +#### Slow execution time + +Running `make` command for the first time can take some time due to building and/or starting all required Docker Compose +services. All subsequent calls will be faster. + +The execution time of Oracle tests is the longest. The recommended flow is to run only changed / added tests. Add +`@group temp` PHPDoc annotation to changed / added tests temporarily. Then you can limit running tests with the +following command: ```shell -./vendor/bin/phpunit --testsuite=Pgsql +make test-driver-oracle RUN_ARGS="--group temp" ``` -Suites available: +Don't forget to remove the temporary `@group` tags before marking PR as ready for review. + +Avoid mixing changes for altering test structure with actual changes in test code. -- Mssql -- Mysql -- Oracle -- Pgsql -- Sqlite +### Mutation testing -## Static analysis +The package tests are checked with Infection mutation framework with Infection Static Analysis Plugin. To run it: + +```shell +make mutation +``` + +### Static analysis The code is statically analyzed with [Psalm](https://psalm.dev/). To run static analysis: ```shell -./vendor/bin/psalm +make static-analysis ``` -## Code style +### Code style Use [Rector](https://github.com/rectorphp/rector) to make codebase follow some specific rules or use either newest or any specific version of PHP: ```shell -./vendor/bin/rector +make rector ``` -## Dependencies +### Dependencies This package uses [composer-require-checker](https://github.com/maglnet/ComposerRequireChecker) to check if all dependencies are correctly defined in `composer.json`. To run the checker, execute the following command: ```shell -./vendor/bin/composer-require-checker +make composer-require-checker ``` + +### Other commands + +- `make help` / `make` - show the list of available commands with description. +- `make build` - build services. +- `make up` - start services. +- `make build-up` - build and start services. +- `make ps` - list running services. +- `make stop` - stop running services. +- `make down` - stop running services and remove all services (not defined services, containers, networks, volumes, +images). +- `make run command` - run arbitrary command. +- `make shell` - open interactive shell.