sail 15 KB

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