sail (17287B)
1 #!/usr/bin/env bash 2 3 UNAMEOUT="$(uname -s)" 4 5 # Verify operating system is supported... 6 case "${UNAMEOUT}" in 7 Linux*) MACHINE=linux;; 8 Darwin*) MACHINE=mac;; 9 *) MACHINE="UNKNOWN" 10 esac 11 12 if [ "$MACHINE" == "UNKNOWN" ]; then 13 echo "Unsupported operating system [$(uname -s)]. Laravel Sail supports macOS, Linux, and Windows (WSL2)." >&2 14 15 exit 1 16 fi 17 18 # Determine if stdout is a terminal... 19 if test -t 1; then 20 # Determine if colors are supported... 21 ncolors=$(tput colors) 22 23 if test -n "$ncolors" && test "$ncolors" -ge 8; then 24 BOLD="$(tput bold)" 25 YELLOW="$(tput setaf 3)" 26 GREEN="$(tput setaf 2)" 27 NC="$(tput sgr0)" 28 fi 29 fi 30 31 # Function that prints the available commands... 32 function display_help { 33 echo "Laravel Sail" 34 echo 35 echo "${YELLOW}Usage:${NC}" >&2 36 echo " sail COMMAND [options] [arguments]" 37 echo 38 echo "Unknown commands are passed to the docker-compose binary." 39 echo 40 echo "${YELLOW}docker-compose Commands:${NC}" 41 echo " ${GREEN}sail up${NC} Start the application" 42 echo " ${GREEN}sail up -d${NC} Start the application in the background" 43 echo " ${GREEN}sail stop${NC} Stop the application" 44 echo " ${GREEN}sail restart${NC} Restart the application" 45 echo " ${GREEN}sail ps${NC} Display the status of all containers" 46 echo 47 echo "${YELLOW}Artisan Commands:${NC}" 48 echo " ${GREEN}sail artisan ...${NC} Run an Artisan command" 49 echo " ${GREEN}sail artisan queue:work${NC}" 50 echo 51 echo "${YELLOW}PHP Commands:${NC}" 52 echo " ${GREEN}sail php ...${NC} Run a snippet of PHP code" 53 echo " ${GREEN}sail php -v${NC}" 54 echo 55 echo "${YELLOW}Composer Commands:${NC}" 56 echo " ${GREEN}sail composer ...${NC} Run a Composer command" 57 echo " ${GREEN}sail composer require laravel/sanctum${NC}" 58 echo 59 echo "${YELLOW}Node Commands:${NC}" 60 echo " ${GREEN}sail node ...${NC} Run a Node command" 61 echo " ${GREEN}sail node --version${NC}" 62 echo 63 echo "${YELLOW}NPM Commands:${NC}" 64 echo " ${GREEN}sail npm ...${NC} Run a npm command" 65 echo " ${GREEN}sail npx${NC} Run a npx command" 66 echo " ${GREEN}sail npm run prod${NC}" 67 echo 68 echo "${YELLOW}PNPM Commands:${NC}" 69 echo " ${GREEN}sail pnpm ...${NC} Run a pnpm command" 70 echo " ${GREEN}sail pnpx${NC} Run a pnpx command" 71 echo " ${GREEN}sail pnpm run prod${NC}" 72 echo 73 echo "${YELLOW}Yarn Commands:${NC}" 74 echo " ${GREEN}sail yarn ...${NC} Run a Yarn command" 75 echo " ${GREEN}sail yarn run prod${NC}" 76 echo 77 echo "${YELLOW}Bun Commands:${NC}" 78 echo " ${GREEN}sail bun ...${NC} Run a bun command" 79 echo " ${GREEN}sail bunx${NC} Run a bunx command" 80 echo " ${GREEN}sail bun run prod${NC}" 81 echo 82 echo "${YELLOW}Database Commands:${NC}" 83 echo " ${GREEN}sail mysql${NC} Start a MySQL CLI session within the 'mysql' container" 84 echo " ${GREEN}sail mariadb${NC} Start a MySQL CLI session within the 'mariadb' container" 85 echo " ${GREEN}sail psql${NC} Start a PostgreSQL CLI session within the 'pgsql' container" 86 echo " ${GREEN}sail redis${NC} Start a Redis CLI session within the 'redis' container" 87 echo 88 echo "${YELLOW}Debugging:${NC}" 89 echo " ${GREEN}sail debug ...${NC} Run an Artisan command in debug mode" 90 echo " ${GREEN}sail debug queue:work${NC}" 91 echo 92 echo "${YELLOW}Running Tests:${NC}" 93 echo " ${GREEN}sail test${NC} Run the PHPUnit tests via the Artisan test command" 94 echo " ${GREEN}sail phpunit ...${NC} Run PHPUnit" 95 echo " ${GREEN}sail pest ...${NC} Run Pest" 96 echo " ${GREEN}sail pint ...${NC} Run Pint" 97 echo " ${GREEN}sail dusk${NC} Run the Dusk tests (Requires the laravel/dusk package)" 98 echo " ${GREEN}sail dusk:fails${NC} Re-run previously failed Dusk tests (Requires the laravel/dusk package)" 99 echo 100 echo "${YELLOW}Container CLI:${NC}" 101 echo " ${GREEN}sail shell${NC} Start a shell session within the application container" 102 echo " ${GREEN}sail bash${NC} Alias for 'sail shell'" 103 echo " ${GREEN}sail root-shell${NC} Start a root shell session within the application container" 104 echo " ${GREEN}sail root-bash${NC} Alias for 'sail root-shell'" 105 echo " ${GREEN}sail tinker${NC} Start a new Laravel Tinker session" 106 echo 107 echo "${YELLOW}Sharing:${NC}" 108 echo " ${GREEN}sail share${NC} Share the application publicly via a temporary URL" 109 echo " ${GREEN}sail open${NC} Open the site in your browser" 110 echo 111 echo "${YELLOW}Binaries:${NC}" 112 echo " ${GREEN}sail bin ...${NC} Run Composer binary scripts from the vendor/bin directory" 113 echo 114 echo "${YELLOW}Customization:${NC}" 115 echo " ${GREEN}sail artisan sail:publish${NC} Publish the Sail configuration files" 116 echo " ${GREEN}sail build --no-cache${NC} Rebuild all of the Sail containers" 117 118 exit 1 119 } 120 121 # Proxy the "help" command... 122 if [ $# -gt 0 ]; then 123 if [ "$1" == "help" ] || [ "$1" == "-h" ] || [ "$1" == "-help" ] || [ "$1" == "--help" ]; then 124 display_help 125 fi 126 else 127 display_help 128 fi 129 130 # Source the ".env" file so Laravel's environment variables are available... 131 if [ ! -z "$APP_ENV" ] && [ -f ./.env.$APP_ENV ]; then 132 source ./.env.$APP_ENV; 133 elif [ -f ./.env ]; then 134 source ./.env; 135 fi 136 137 # Define environment variables... 138 export APP_PORT=${APP_PORT:-80} 139 export APP_SERVICE=${APP_SERVICE:-"laravel.test"} 140 export DB_PORT=${DB_PORT:-3306} 141 export WWWUSER=${WWWUSER:-$UID} 142 export WWWGROUP=${WWWGROUP:-$(id -g)} 143 144 export SAIL_FILES=${SAIL_FILES:-""} 145 export SAIL_SHARE_DASHBOARD=${SAIL_SHARE_DASHBOARD:-4040} 146 export SAIL_SHARE_SERVER_HOST=${SAIL_SHARE_SERVER_HOST:-"laravel-sail.site"} 147 export SAIL_SHARE_SERVER_PORT=${SAIL_SHARE_SERVER_PORT:-8080} 148 export SAIL_SHARE_SUBDOMAIN=${SAIL_SHARE_SUBDOMAIN:-""} 149 export SAIL_SHARE_DOMAIN=${SAIL_SHARE_DOMAIN:-"$SAIL_SHARE_SERVER_HOST"} 150 export SAIL_SHARE_SERVER=${SAIL_SHARE_SERVER:-""} 151 152 # Function that outputs Sail is not running... 153 function sail_is_not_running { 154 echo "${BOLD}Sail is not running.${NC}" >&2 155 echo "" >&2 156 echo "${BOLD}You may Sail using the following commands:${NC} './vendor/bin/sail up' or './vendor/bin/sail up -d'" >&2 157 158 exit 1 159 } 160 161 # Define Docker Compose command prefix... 162 docker compose &> /dev/null 163 if [ $? == 0 ]; then 164 DOCKER_COMPOSE=(docker compose) 165 else 166 DOCKER_COMPOSE=(docker-compose) 167 fi 168 169 if [ -n "$SAIL_FILES" ]; then 170 # Convert SAIL_FILES to an array... 171 IFS=':' read -ra SAIL_FILES <<< "$SAIL_FILES" 172 173 for FILE in "${SAIL_FILES[@]}"; do 174 if [ -f "$FILE" ]; then 175 DOCKER_COMPOSE+=(-f "$FILE") 176 else 177 echo "${BOLD}Unable to find Docker Compose file: '${FILE}'${NC}" >&2 178 179 exit 1 180 fi 181 done 182 fi 183 184 EXEC="yes" 185 186 if [ -z "$SAIL_SKIP_CHECKS" ]; then 187 # Ensure that Docker is running... 188 if ! docker info > /dev/null 2>&1; then 189 echo "${BOLD}Docker is not running.${NC}" >&2 190 191 exit 1 192 fi 193 194 # Determine if Sail is currently up... 195 if "${DOCKER_COMPOSE[@]}" ps "$APP_SERVICE" 2>&1 | grep 'Exit\|exited'; then 196 echo "${BOLD}Shutting down old Sail processes...${NC}" >&2 197 198 "${DOCKER_COMPOSE[@]}" down > /dev/null 2>&1 199 200 EXEC="no" 201 elif [ -z "$("${DOCKER_COMPOSE[@]}" ps -q)" ]; then 202 EXEC="no" 203 fi 204 fi 205 206 ARGS=() 207 208 # Proxy PHP commands to the "php" binary on the application container... 209 if [ "$1" == "php" ]; then 210 shift 1 211 212 if [ "$EXEC" == "yes" ]; then 213 ARGS+=(exec -u sail) 214 [ ! -t 0 ] && ARGS+=(-T) 215 ARGS+=("$APP_SERVICE" "php" "$@") 216 else 217 sail_is_not_running 218 fi 219 220 # Proxy vendor binary commands on the application container... 221 elif [ "$1" == "bin" ]; then 222 shift 1 223 224 if [ "$EXEC" == "yes" ]; then 225 ARGS+=(exec -u sail) 226 [ ! -t 0 ] && ARGS+=(-T) 227 ARGS+=("$APP_SERVICE" ./vendor/bin/"$@") 228 else 229 sail_is_not_running 230 fi 231 232 # Proxy docker-compose commands to the docker-compose binary on the application container... 233 elif [ "$1" == "docker-compose" ]; then 234 shift 1 235 236 if [ "$EXEC" == "yes" ]; then 237 ARGS+=(exec -u sail) 238 [ ! -t 0 ] && ARGS+=(-T) 239 ARGS+=("$APP_SERVICE" "${DOCKER_COMPOSE[@]}") 240 else 241 sail_is_not_running 242 fi 243 244 # Proxy Composer commands to the "composer" binary on the application container... 245 elif [ "$1" == "composer" ]; then 246 shift 1 247 248 if [ "$EXEC" == "yes" ]; then 249 ARGS+=(exec -u sail) 250 [ ! -t 0 ] && ARGS+=(-T) 251 ARGS+=("$APP_SERVICE" "composer" "$@") 252 else 253 sail_is_not_running 254 fi 255 256 # Proxy Artisan commands to the "artisan" binary on the application container... 257 elif [ "$1" == "artisan" ] || [ "$1" == "art" ] || [ "$1" == "a" ]; then 258 shift 1 259 260 if [ "$EXEC" == "yes" ]; then 261 ARGS+=(exec -u sail) 262 [ ! -t 0 ] && ARGS+=(-T) 263 ARGS+=("$APP_SERVICE" php artisan "$@") 264 else 265 sail_is_not_running 266 fi 267 268 # Proxy the "debug" command to the "php artisan" binary on the application container with xdebug enabled... 269 elif [ "$1" == "debug" ]; then 270 shift 1 271 272 if [ "$EXEC" == "yes" ]; then 273 ARGS+=(exec -u sail -e XDEBUG_SESSION=1) 274 [ ! -t 0 ] && ARGS+=(-T) 275 ARGS+=("$APP_SERVICE" php artisan "$@") 276 else 277 sail_is_not_running 278 fi 279 280 # Proxy the "test" command to the "php artisan test" Artisan command... 281 elif [ "$1" == "test" ]; then 282 shift 1 283 284 if [ "$EXEC" == "yes" ]; then 285 ARGS+=(exec -u sail) 286 [ ! -t 0 ] && ARGS+=(-T) 287 ARGS+=("$APP_SERVICE" php artisan test "$@") 288 else 289 sail_is_not_running 290 fi 291 292 # Proxy the "phpunit" command to "php vendor/bin/phpunit"... 293 elif [ "$1" == "phpunit" ]; then 294 shift 1 295 296 if [ "$EXEC" == "yes" ]; then 297 ARGS+=(exec -u sail) 298 [ ! -t 0 ] && ARGS+=(-T) 299 ARGS+=("$APP_SERVICE" php vendor/bin/phpunit "$@") 300 else 301 sail_is_not_running 302 fi 303 304 # Proxy the "pest" command to "php vendor/bin/pest"... 305 elif [ "$1" == "pest" ]; then 306 shift 1 307 308 if [ "$EXEC" == "yes" ]; then 309 ARGS+=(exec -u sail) 310 [ ! -t 0 ] && ARGS+=(-T) 311 ARGS+=("$APP_SERVICE" php vendor/bin/pest "$@") 312 else 313 sail_is_not_running 314 fi 315 316 # Proxy the "pint" command to "php vendor/bin/pint"... 317 elif [ "$1" == "pint" ]; then 318 shift 1 319 320 if [ "$EXEC" == "yes" ]; then 321 ARGS+=(exec -u sail) 322 [ ! -t 0 ] && ARGS+=(-T) 323 ARGS+=("$APP_SERVICE" php vendor/bin/pint "$@") 324 else 325 sail_is_not_running 326 fi 327 328 # Proxy the "dusk" command to the "php artisan dusk" Artisan command... 329 elif [ "$1" == "dusk" ]; then 330 shift 1 331 332 if [ "$EXEC" == "yes" ]; then 333 ARGS+=(exec -u sail) 334 [ ! -t 0 ] && ARGS+=(-T) 335 ARGS+=(-e "APP_URL=http://${APP_SERVICE}") 336 ARGS+=(-e "DUSK_DRIVER_URL=http://selenium:4444/wd/hub") 337 ARGS+=("$APP_SERVICE" php artisan dusk "$@") 338 else 339 sail_is_not_running 340 fi 341 342 # Proxy the "dusk:fails" command to the "php artisan dusk:fails" Artisan command... 343 elif [ "$1" == "dusk:fails" ]; then 344 shift 1 345 346 if [ "$EXEC" == "yes" ]; then 347 ARGS+=(exec -u sail) 348 [ ! -t 0 ] && ARGS+=(-T) 349 ARGS+=(-e "APP_URL=http://${APP_SERVICE}") 350 ARGS+=(-e "DUSK_DRIVER_URL=http://selenium:4444/wd/hub") 351 ARGS+=("$APP_SERVICE" php artisan dusk:fails "$@") 352 else 353 sail_is_not_running 354 fi 355 356 # Initiate a Laravel Tinker session within the application container... 357 elif [ "$1" == "tinker" ] ; then 358 shift 1 359 360 if [ "$EXEC" == "yes" ]; then 361 ARGS+=(exec -u sail) 362 [ ! -t 0 ] && ARGS+=(-T) 363 ARGS+=("$APP_SERVICE" php artisan tinker) 364 else 365 sail_is_not_running 366 fi 367 368 # Proxy Node commands to the "node" binary on the application container... 369 elif [ "$1" == "node" ]; then 370 shift 1 371 372 if [ "$EXEC" == "yes" ]; then 373 ARGS+=(exec -u sail) 374 [ ! -t 0 ] && ARGS+=(-T) 375 ARGS+=("$APP_SERVICE" node "$@") 376 else 377 sail_is_not_running 378 fi 379 380 # Proxy NPM commands to the "npm" binary on the application container... 381 elif [ "$1" == "npm" ]; then 382 shift 1 383 384 if [ "$EXEC" == "yes" ]; then 385 ARGS+=(exec -u sail) 386 [ ! -t 0 ] && ARGS+=(-T) 387 ARGS+=("$APP_SERVICE" npm "$@") 388 else 389 sail_is_not_running 390 fi 391 392 # Proxy NPX commands to the "npx" binary on the application container... 393 elif [ "$1" == "npx" ]; then 394 shift 1 395 396 if [ "$EXEC" == "yes" ]; then 397 ARGS+=(exec -u sail) 398 [ ! -t 0 ] && ARGS+=(-T) 399 ARGS+=("$APP_SERVICE" npx "$@") 400 else 401 sail_is_not_running 402 fi 403 404 # Proxy PNPM commands to the "pnpm" binary on the application container... 405 elif [ "$1" == "pnpm" ]; then 406 shift 1 407 408 if [ "$EXEC" == "yes" ]; then 409 ARGS+=(exec -u sail) 410 [ ! -t 0 ] && ARGS+=(-T) 411 ARGS+=("$APP_SERVICE" pnpm "$@") 412 else 413 sail_is_not_running 414 fi 415 416 # Proxy PNPX commands to the "pnpx" binary on the application container... 417 elif [ "$1" == "pnpx" ]; then 418 shift 1 419 420 if [ "$EXEC" == "yes" ]; then 421 ARGS+=(exec -u sail) 422 [ ! -t 0 ] && ARGS+=(-T) 423 ARGS+=("$APP_SERVICE" pnpx "$@") 424 else 425 sail_is_not_running 426 fi 427 428 # Proxy YARN commands to the "yarn" binary on the application container... 429 elif [ "$1" == "yarn" ]; then 430 shift 1 431 432 if [ "$EXEC" == "yes" ]; then 433 ARGS+=(exec -u sail) 434 [ ! -t 0 ] && ARGS+=(-T) 435 ARGS+=("$APP_SERVICE" yarn "$@") 436 else 437 sail_is_not_running 438 fi 439 440 # Proxy Bun commands to the "bun" binary on the application container... 441 elif [ "$1" == "bun" ]; then 442 shift 1 443 444 if [ "$EXEC" == "yes" ]; then 445 ARGS+=(exec -u sail) 446 [ ! -t 0 ] && ARGS+=(-T) 447 ARGS+=("$APP_SERVICE" bun "$@") 448 else 449 sail_is_not_running 450 fi 451 452 # Proxy Bun X commands to the "bunx" binary on the application container... 453 elif [ "$1" == "bunx" ]; then 454 shift 1 455 456 if [ "$EXEC" == "yes" ]; then 457 ARGS+=(exec -u sail) 458 [ ! -t 0 ] && ARGS+=(-T) 459 ARGS+=("$APP_SERVICE" bunx "$@") 460 else 461 sail_is_not_running 462 fi 463 464 # Initiate a MySQL CLI terminal session within the "mysql" container... 465 elif [ "$1" == "mysql" ]; then 466 shift 1 467 468 if [ "$EXEC" == "yes" ]; then 469 ARGS+=(exec) 470 [ ! -t 0 ] && ARGS+=(-T) 471 ARGS+=(mysql bash -c) 472 ARGS+=("MYSQL_PWD=\${MYSQL_PASSWORD} mysql -u \${MYSQL_USER} \${MYSQL_DATABASE}") 473 else 474 sail_is_not_running 475 fi 476 477 # Initiate a MySQL CLI terminal session within the "mariadb" container... 478 elif [ "$1" == "mariadb" ]; then 479 shift 1 480 481 if [ "$EXEC" == "yes" ]; then 482 ARGS+=(exec) 483 [ ! -t 0 ] && ARGS+=(-T) 484 ARGS+=(mariadb bash -c) 485 ARGS+=("MYSQL_PWD=\${MYSQL_PASSWORD} mysql -u \${MYSQL_USER} \${MYSQL_DATABASE}") 486 else 487 sail_is_not_running 488 fi 489 490 # Initiate a PostgreSQL CLI terminal session within the "pgsql" container... 491 elif [ "$1" == "psql" ]; then 492 shift 1 493 494 if [ "$EXEC" == "yes" ]; then 495 ARGS+=(exec) 496 [ ! -t 0 ] && ARGS+=(-T) 497 ARGS+=(pgsql bash -c) 498 ARGS+=("PGPASSWORD=\${PGPASSWORD} psql -U \${POSTGRES_USER} \${POSTGRES_DB}") 499 else 500 sail_is_not_running 501 fi 502 503 # Initiate a Bash shell within the application container... 504 elif [ "$1" == "shell" ] || [ "$1" == "bash" ]; then 505 shift 1 506 507 if [ "$EXEC" == "yes" ]; then 508 ARGS+=(exec -u sail) 509 [ ! -t 0 ] && ARGS+=(-T) 510 ARGS+=("$APP_SERVICE" bash "$@") 511 else 512 sail_is_not_running 513 fi 514 515 # Initiate a root user Bash shell within the application container... 516 elif [ "$1" == "root-shell" ] || [ "$1" == "root-bash" ]; then 517 shift 1 518 519 if [ "$EXEC" == "yes" ]; then 520 ARGS+=(exec) 521 [ ! -t 0 ] && ARGS+=(-T) 522 ARGS+=("$APP_SERVICE" bash "$@") 523 else 524 sail_is_not_running 525 fi 526 527 # Initiate a Redis CLI terminal session within the "redis" container... 528 elif [ "$1" == "redis" ] ; then 529 shift 1 530 531 if [ "$EXEC" == "yes" ]; then 532 ARGS+=(exec) 533 [ ! -t 0 ] && ARGS+=(-T) 534 ARGS+=(redis redis-cli) 535 else 536 sail_is_not_running 537 fi 538 539 # Share the site... 540 elif [ "$1" == "share" ]; then 541 shift 1 542 543 if [ "$EXEC" == "yes" ]; then 544 docker run --init --rm -p "$SAIL_SHARE_DASHBOARD":4040 -t beyondcodegmbh/expose-server:latest share http://host.docker.internal:"$APP_PORT" \ 545 --server-host="$SAIL_SHARE_SERVER_HOST" \ 546 --server-port="$SAIL_SHARE_SERVER_PORT" \ 547 --auth="$SAIL_SHARE_TOKEN" \ 548 --server="$SAIL_SHARE_SERVER" \ 549 --subdomain="$SAIL_SHARE_SUBDOMAIN" \ 550 --domain="$SAIL_SHARE_DOMAIN" \ 551 "$@" 552 553 exit 554 else 555 sail_is_not_running 556 fi 557 558 # Open the site... 559 elif [ "$1" == "open" ]; then 560 shift 1 561 562 if [ "$EXEC" == "yes" ]; then 563 open $APP_URL 564 565 exit 566 else 567 sail_is_not_running 568 fi 569 570 # Pass unknown commands to the "docker-compose" binary... 571 else 572 ARGS+=("$@") 573 fi 574 575 # Run Docker Compose with the defined arguments... 576 "${DOCKER_COMPOSE[@]}" "${ARGS[@]}"