diff --git a/bin/sail b/bin/sail index 32b935c7..44ab5da4 100755 --- a/bin/sail +++ b/bin/sail @@ -83,6 +83,7 @@ function display_help { echo " ${GREEN}sail mysql${NC} Start a MySQL CLI session within the 'mysql' container" echo " ${GREEN}sail mariadb${NC} Start a MySQL CLI session within the 'mariadb' container" echo " ${GREEN}sail psql${NC} Start a PostgreSQL CLI session within the 'pgsql' container" + echo " ${GREEN}sail sqlsrv${NC} Start a Microsoft SQL Server CLI session within the 'sqlsrv' container" echo " ${GREEN}sail redis${NC} Start a Redis CLI session within the 'redis' container" echo echo "${YELLOW}Debugging:${NC}" @@ -500,6 +501,19 @@ elif [ "$1" == "psql" ]; then sail_is_not_running fi +# Initiate a Microsoft SQL Server CLI terminal session within the "sqlsrv" container... +elif [ "$1" == "sqlsrv" ]; then + shift 1 + + if [ "$EXEC" == "yes" ]; then + ARGS+=(exec) + [ ! -t 0 ] && ARGS+=(-T) + ARGS+=(sqlcmd bash -c) + ARGS+=("sqlcmd -No -d \${MSSQL_DB_NAME} -P \${MSSQL_PASSWORD} -U \${MSSQL_USER}") + else + sail_is_not_running + fi + # Initiate a Bash shell within the application container... elif [ "$1" == "shell" ] || [ "$1" == "bash" ]; then shift 1 diff --git a/database/sqlsrv/entrypoint.sh b/database/sqlsrv/entrypoint.sh new file mode 100755 index 00000000..d8a7d690 --- /dev/null +++ b/database/sqlsrv/entrypoint.sh @@ -0,0 +1,92 @@ +#!/usr/bin/env bash + +# If there is a command, execute it. +if [ "$#" -ne 0 ]; then + exec "$@" +fi + +SQLCMD="/opt/mssql-tools/bin/sqlcmd" + +if [ -f /opt/mssql-tools18/bin/sqlcmd ]; then + SQLCMD="/opt/mssql-tools18/bin/sqlcmd" +fi + +echo "Laravel Sail: Starting Microsoft SQL Server for init scripts." + +# Start SQL Server +/opt/mssql/bin/sqlservr -mSQLCMD -f -c -x & + +# Function to check if SQL Server is ready +sql_server_is_ready() { + $SQLCMD -No -U sa -P "${MSSQL_SA_PASSWORD}" -Q "SELECT 1" &> /dev/null + return $? +} + +# Wait until SQL Server is ready +until sql_server_is_ready; do + echo "Laravel Sail: Waiting for SQL Server to be available..." + sleep 2 +done + +SQL_INIT=$(cat <<-EOSQL + IF NOT EXISTS(SELECT * FROM sys.databases WHERE name = "$MSSQL_DB_NAME") BEGIN + CREATE DATABASE $MSSQL_DB_NAME; + END + GO + + IF NOT EXISTS (SELECT name FROM master.sys.server_principals WHERE name = "$MSSQL_USER") BEGIN + CREATE LOGIN $MSSQL_USER WITH + PASSWORD = "$MSSQL_PASSWORD", + DEFAULT_DATABASE = $MSSQL_DB_NAME, + CHECK_EXPIRATION = OFF, + CHECK_POLICY = OFF; + END + GO + + IF NOT EXISTS(SELECT * FROM sys.database_principals WHERE name = "$MSSQL_USER") BEGIN + CREATE USER $MSSQL_USER FOR LOGIN $MSSQL_USER; + ALTER ROLE db_owner ADD MEMBER $MSSQL_USER; + END + GO +EOSQL +) + +echo "Laravel Sail: Initializing ${MSSQL_DB_NAME} database for ${MSSQL_USER}." + +## Set the engine default user and database name +$SQLCMD -No -U sa -No -P "${MSSQL_SA_PASSWORD}" -Q "${SQL_INIT}" + +SQL_INIT_TESTING=$(cat <<-EOSQL + IF NOT EXISTS(SELECT * FROM sys.databases WHERE name = "testing") BEGIN + CREATE DATABASE testing; + END + GO + + IF NOT EXISTS(SELECT * FROM sys.database_principals WHERE name = "testing") BEGIN + CREATE USER testing FOR LOGIN testing WITH + PASSWORD = "", + DEFAULT_DATABASE = "testing", + CHECK_EXPIRATION = OFF, + CHECK_POLICY = OFF; + ALTER ROLE db_datareader ADD MEMBER testing; + ALTER ROLE db_datawriter ADD MEMBER testing; + ALTER ROLE db_ddladmin ADD MEMBER testing; + END + GO +EOSQL +) + +echo "Laravel Sail: Initializing testing database." + +## Create the testing database if it doesn't exists +$SQLCMD -No -U sa -P "${MSSQL_SA_PASSWORD}" -Q "${SQL_INIT_TESTING}" + +echo "Laravel Sail: Stopping Microsoft SQL Server for init scripts." + +# Kill the server +pkill sqlservr + +echo "Laravel Sail: Starting Microsoft SQL Server." + +# Start it again. +exec /opt/mssql/bin/sqlservr diff --git a/runtimes/8.0/Dockerfile b/runtimes/8.0/Dockerfile index e95e340c..0b93eaef 100644 --- a/runtimes/8.0/Dockerfile +++ b/runtimes/8.0/Dockerfile @@ -5,6 +5,8 @@ LABEL maintainer="Taylor Otwell" ARG WWWGROUP ARG NODE_VERSION=20 ARG POSTGRES_VERSION=13 +ARG SQLSRV_VERSION=5.11.1 +ARG ACCEPT_EULA="N" WORKDIR /var/www/html @@ -48,6 +50,22 @@ RUN apt-get update \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* +RUN if [ "$ACCEPT_EULA" = "Y" ]; then \ + curl -sS https://packages.microsoft.com/keys/microsoft.asc | tee /etc/apt/trusted.gpg.d/microsoft.asc \ + && curl -sS https://packages.microsoft.com/config/ubuntu/20.04/prod.list | tee /etc/apt/sources.list.d/mssql-release.list \ + && apt-get update \ + && apt-get install -y mssql-tools18 unixodbc-dev \ + && echo 'export PATH="$PATH:/opt/mssql-tools18/bin"' >> ~/.bashrc \ + && pecl install sqlsrv pdo_sqlsrv-$SQLSRV_VERSION \ + && printf "; priority=20\nextension=sqlsrv.so\n" > /etc/php/8.0/mods-available/sqlsrv.ini \ + && printf "; priority=30\nextension=pdo_sqlsrv.so\n" > /etc/php/8.0/mods-available/pdo_sqlsrv.ini \ + && phpenmod sqlsrv pdo_sqlsrv \ + && bash -c "source ~/.bashrc" \ + && apt-get -y autoremove \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*; \ + fi + RUN update-alternatives --set php /usr/bin/php8.0 RUN setcap "cap_net_bind_service=+ep" /usr/bin/php8.0 diff --git a/runtimes/8.1/Dockerfile b/runtimes/8.1/Dockerfile index c2fae853..bd5753c6 100644 --- a/runtimes/8.1/Dockerfile +++ b/runtimes/8.1/Dockerfile @@ -5,6 +5,8 @@ LABEL maintainer="Taylor Otwell" ARG WWWGROUP ARG NODE_VERSION=20 ARG POSTGRES_VERSION=15 +ARG SQLSRV_VERSION=5.12.0 +ARG ACCEPT_EULA="N" WORKDIR /var/www/html @@ -49,6 +51,22 @@ RUN apt-get update \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* +RUN if [ "$ACCEPT_EULA" = "Y" ]; then \ + curl -sS https://packages.microsoft.com/keys/microsoft.asc | tee /etc/apt/trusted.gpg.d/microsoft.asc \ + && curl -sS https://packages.microsoft.com/config/ubuntu/22.04/prod.list | tee /etc/apt/sources.list.d/mssql-release.list \ + && apt-get update \ + && apt-get install -y mssql-tools18 unixodbc-dev \ + && echo 'export PATH="$PATH:/opt/mssql-tools18/bin"' >> ~/.bashrc \ + && pecl install sqlsrv pdo_sqlsrv-$SQLSRV_VERSION \ + && printf "; priority=20\nextension=sqlsrv.so\n" > /etc/php/8.1/mods-available/sqlsrv.ini \ + && printf "; priority=30\nextension=pdo_sqlsrv.so\n" > /etc/php/8.1/mods-available/pdo_sqlsrv.ini \ + && phpenmod sqlsrv pdo_sqlsrv \ + && bash -c "source ~/.bashrc" \ + && apt-get -y autoremove \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*; \ + fi + RUN setcap "cap_net_bind_service=+ep" /usr/bin/php8.1 RUN groupadd --force -g $WWWGROUP sail diff --git a/runtimes/8.2/Dockerfile b/runtimes/8.2/Dockerfile index 270a8b5a..7e08444a 100644 --- a/runtimes/8.2/Dockerfile +++ b/runtimes/8.2/Dockerfile @@ -5,6 +5,8 @@ LABEL maintainer="Taylor Otwell" ARG WWWGROUP ARG NODE_VERSION=20 ARG POSTGRES_VERSION=15 +ARG SQLSRV_VERSION=5.12.0 +ARG ACCEPT_EULA="N" WORKDIR /var/www/html @@ -50,6 +52,22 @@ RUN apt-get update \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* +RUN if [ "$ACCEPT_EULA" = "Y" ]; then \ + curl -sS https://packages.microsoft.com/keys/microsoft.asc | tee /etc/apt/trusted.gpg.d/microsoft.asc \ + && curl -sS https://packages.microsoft.com/config/ubuntu/22.04/prod.list | tee /etc/apt/sources.list.d/mssql-release.list \ + && apt-get update \ + && apt-get install -y mssql-tools18 unixodbc-dev \ + && echo 'export PATH="$PATH:/opt/mssql-tools18/bin"' >> ~/.bashrc \ + && pecl install sqlsrv pdo_sqlsrv-$SQLSRV_VERSION \ + && printf "; priority=20\nextension=sqlsrv.so\n" > /etc/php/8.2/mods-available/sqlsrv.ini \ + && printf "; priority=30\nextension=pdo_sqlsrv.so\n" > /etc/php/8.2/mods-available/pdo_sqlsrv.ini \ + && phpenmod sqlsrv pdo_sqlsrv \ + && bash -c "source ~/.bashrc" \ + && apt-get -y autoremove \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*; \ + fi + RUN setcap "cap_net_bind_service=+ep" /usr/bin/php8.2 RUN groupadd --force -g $WWWGROUP sail diff --git a/runtimes/8.3/Dockerfile b/runtimes/8.3/Dockerfile index 2a611db1..23735d05 100644 --- a/runtimes/8.3/Dockerfile +++ b/runtimes/8.3/Dockerfile @@ -6,6 +6,8 @@ ARG WWWGROUP ARG NODE_VERSION=20 ARG MYSQL_CLIENT="mysql-client" ARG POSTGRES_VERSION=15 +ARG SQLSRV_VERSION=5.12.0 +ARG ACCEPT_EULA="N" WORKDIR /var/www/html @@ -51,6 +53,22 @@ RUN apt-get update \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* +RUN if [ "$ACCEPT_EULA" = "Y" ]; then \ + curl -sS https://packages.microsoft.com/keys/microsoft.asc | tee /etc/apt/trusted.gpg.d/microsoft.asc \ + && curl -sS https://packages.microsoft.com/config/ubuntu/22.04/prod.list | tee /etc/apt/sources.list.d/mssql-release.list \ + && apt-get update \ + && apt-get install -y mssql-tools18 unixodbc-dev \ + && echo 'export PATH="$PATH:/opt/mssql-tools18/bin"' >> ~/.bashrc \ + && pecl install sqlsrv pdo_sqlsrv-$SQLSRV_VERSION \ + && printf "; priority=20\nextension=sqlsrv.so\n" > /etc/php/8.3/mods-available/sqlsrv.ini \ + && printf "; priority=30\nextension=pdo_sqlsrv.so\n" > /etc/php/8.3/mods-available/pdo_sqlsrv.ini \ + && phpenmod sqlsrv pdo_sqlsrv \ + && bash -c "source ~/.bashrc" \ + && apt-get -y autoremove \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*; \ + fi + RUN setcap "cap_net_bind_service=+ep" /usr/bin/php8.3 RUN groupadd --force -g $WWWGROUP sail diff --git a/src/Console/Concerns/InteractsWithDockerComposeServices.php b/src/Console/Concerns/InteractsWithDockerComposeServices.php index 0404525b..4dbccb5f 100644 --- a/src/Console/Concerns/InteractsWithDockerComposeServices.php +++ b/src/Console/Concerns/InteractsWithDockerComposeServices.php @@ -2,8 +2,10 @@ namespace Laravel\Sail\Console\Concerns; +use RuntimeException; use Symfony\Component\Process\Process; use Symfony\Component\Yaml\Yaml; +use function str_replace; trait InteractsWithDockerComposeServices { @@ -16,6 +18,7 @@ trait InteractsWithDockerComposeServices 'mysql', 'pgsql', 'mariadb', + 'sqlsrv', 'redis', 'memcached', 'meilisearch', @@ -99,7 +102,7 @@ protected function buildDockerCompose(array $services) // Merge volumes... collect($services) ->filter(function ($service) { - return in_array($service, ['mysql', 'pgsql', 'mariadb', 'redis', 'meilisearch', 'typesense', 'minio']); + return in_array($service, ['mysql', 'pgsql', 'mariadb', 'sqlsrv', 'redis', 'meilisearch', 'typesense', 'minio']); })->filter(function ($service) use ($compose) { return ! array_key_exists($service, $compose['volumes'] ?? []); })->each(function ($service) use (&$compose) { @@ -131,7 +134,8 @@ protected function replaceEnvVariables(array $services) if (in_array('mysql', $services) || in_array('mariadb', $services) || - in_array('pgsql', $services)) { + in_array('pgsql', $services) || + in_array('sqlsrv', $services)) { $defaults = [ '# DB_HOST=127.0.0.1', '# DB_PORT=3306', @@ -148,7 +152,7 @@ protected function replaceEnvVariables(array $services) if (in_array('mysql', $services)) { $environment = preg_replace('/DB_CONNECTION=.*/', 'DB_CONNECTION=mysql', $environment); $environment = str_replace('DB_HOST=127.0.0.1', "DB_HOST=mysql", $environment); - }elseif (in_array('pgsql', $services)) { + } elseif (in_array('pgsql', $services)) { $environment = preg_replace('/DB_CONNECTION=.*/', 'DB_CONNECTION=pgsql', $environment); $environment = str_replace('DB_HOST=127.0.0.1', "DB_HOST=pgsql", $environment); $environment = str_replace('DB_PORT=3306', "DB_PORT=5432", $environment); @@ -158,6 +162,27 @@ protected function replaceEnvVariables(array $services) } $environment = str_replace('DB_HOST=127.0.0.1', "DB_HOST=mariadb", $environment); + } elseif (in_array('sqlsrv', $services)) { + $confirm = $this->confirm( + "To install Microsoft SQL Server 2022 x64, EULA acceptance is required. Accept? \n" . + 'The Microsoft SQL Server EULA is available at: https://mcr.microsoft.com/product/mssql/server/about' + ); + + if (!$confirm) { + throw new RuntimeException('Cannot install Microsoft SQL Server without accepting the EULA.'); + } + + $environment = preg_replace('/DB_CONNECTION=.*/', "DB_CONNECTION=sqlsrv", $environment); + $environment = str_replace('DB_HOST=127.0.0.1', "DB_HOST=sqlsrv", $environment); + $environment = str_replace('DB_PORT=3306', "DB_PORT=1433", $environment); + + if (!preg_match('/ACCEPT_EULA=(.*)/', $environment)) { + $environment = str_replace('DB_CONNECTION=sqlsrv', "DB_CONNECTION=sqlsrv\nACCEPT_EULA=Y", $environment); + } + + if (!preg_match('/DB_ROOT_PASSWORD=(.*)/', $environment)) { + $environment = str_replace('DB_CONNECTION=sqlsrv', "DB_CONNECTION=sqlsrv\nDB_ROOT_PASSWORD=p@ssw0rd", $environment); + } } $environment = str_replace('DB_USERNAME=root', "DB_USERNAME=sail", $environment); diff --git a/src/Console/PublishCommand.php b/src/Console/PublishCommand.php index 8ae11d75..0abf2959 100644 --- a/src/Console/PublishCommand.php +++ b/src/Console/PublishCommand.php @@ -41,7 +41,8 @@ public function handle() './vendor/laravel/sail/runtimes/8.1', './vendor/laravel/sail/runtimes/8.0', './vendor/laravel/sail/database/mysql', - './vendor/laravel/sail/database/pgsql' + './vendor/laravel/sail/database/pgsql', + './vendor/laravel/sail/database/sqlsrv', ], [ './docker/8.3', @@ -49,7 +50,8 @@ public function handle() './docker/8.1', './docker/8.0', './docker/mysql', - './docker/pgsql' + './docker/pgsql', + './docker/sqlsrv', ], file_get_contents($this->laravel->basePath('docker-compose.yml')) ) diff --git a/stubs/docker-compose.stub b/stubs/docker-compose.stub index 9619b379..3d08e981 100644 --- a/stubs/docker-compose.stub +++ b/stubs/docker-compose.stub @@ -6,6 +6,7 @@ services: dockerfile: Dockerfile args: WWWGROUP: '${WWWGROUP}' + ACCEPT_EULA: '${ACCEPT_EULA}' image: sail-8.3/app extra_hosts: - 'host.docker.internal:host-gateway' diff --git a/stubs/sqlsrv.stub b/stubs/sqlsrv.stub new file mode 100644 index 00000000..908512ef --- /dev/null +++ b/stubs/sqlsrv.stub @@ -0,0 +1,22 @@ +sqlsrv: + image: 'mcr.microsoft.com/mssql/server:2022-latest' + ports: + - '${FORWARD_DB_PORT:-1433}:1433' + environment: + MSSQL_DB_NAME: '${DB_DATABASE}' + MSSQL_USER: '${DB_USERNAME}' + MSSQL_SA_PASSWORD: '${DB_ROOT_PASSWORD}' + MSSQL_PASSWORD: '${DB_PASSWORD}' + ACCEPT_EULA: '${ACCEPT_EULA}' + volumes: + - 'sail-sqlsrv:/var/opt/mssql' + - './vendor/laravel/sail/database/sqlsrv/entrypoint.sh:/entrypoint.sh' + entrypoint: "/entrypoint.sh" + networks: + - sail + healthcheck: + test: ["CMD", "/opt/mssql-tools/bin/sqlcmd", "-No", "-U", "sa", "-P", "${MSSQL_SA_PASSWORD}", "-Q", "\"SELECT 1\"", "-b", "-o", "/dev/null"] + interval: 10s + timeout: 5s + retries: 3 + start_period: 30s