From e8cad22fd5d9fb4dc026e1ed92bb67b5df9a257f Mon Sep 17 00:00:00 2001 From: Dane Everitt Date: Sat, 12 Feb 2022 12:54:38 -0500 Subject: [PATCH] More documentation and fixing up things --- .gitignore | 5 - README.md | 60 +++++----- Vagrantfile | 143 ----------------------- beak | 22 ++++ build/panel/Dockerfile | 5 +- build/panel/entrypoint | 8 +- build/panel/{setup => setup-pterodactyl} | 2 - build/wings/Dockerfile | 1 + docker-compose.yml | 59 ++++++++-- docker-sync.yml | 7 -- scripts/deploy_docs.sh | 26 ----- scripts/provision_wings.sh | 44 ------- setup.sh | 51 ++++++-- 13 files changed, 153 insertions(+), 280 deletions(-) delete mode 100644 Vagrantfile create mode 100755 beak rename build/panel/{setup => setup-pterodactyl} (85%) delete mode 100644 docker-sync.yml delete mode 100644 scripts/deploy_docs.sh delete mode 100644 scripts/provision_wings.sh diff --git a/.gitignore b/.gitignore index fb1031f..e66c8b5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,5 @@ -.vagrant/ **/.DS_Store -.data/ -.docker-sync/** .idea -# intended for per-machine config -vagrant.config.yml docker/certificates/* !docker/certificates/.gitkeep diff --git a/README.md b/README.md index 2a71e51..b526d5a 100644 --- a/README.md +++ b/README.md @@ -1,37 +1,45 @@ -# Pterodactyl Vagrant Environment -This repository provides a basic vagrant environment suitable for development of Pterodactyl on your local machine. +# Pterodactyl Development Environment +This repository provides a `docker-compose` based environment for handling local development of Pterodactyl. + +**This is not meant for production use! This is a local development environment only.** + +> This environment is the official Pterodactyl development environment, in the sense that it is what +I, [`@DaneEveritt`](https://github.com/DaneEveritt) use for working on it. I've not tested it on anything +other than macOS, and I probably haven't documented most of the important bits. Please feel free to open +PRs or Issues as necessary to improve this environment. ### Getting Started You'll need the following things installed on your machine. -* Vagrant -* VirtualBox -* Docker -* mkcert +* [Docker](https://docker.io) +* [Mutagen Compose](https://github.com/mutagen-io/mutagen-compose) +* [mkcert](https://github.com/FiloSottile/mkcert) -You'll also need the following Vagrant plugins: `vagrant-hostmanager` and `vagrant-vbguest` +### Setup +To begin clone this repository to your system, and then run `./setup.sh` which will configure the +additional git repositories, and setup your local certificates and host file routing. -### First Run -These commands should be run if this will be your first time running Pterodactyl and you _do not already_ have a `.env` file configured. -``` -vagrant up -vagrant up --provision-with setup app +```sh +git clone https://github.com/pterodactyl/development.git +cd development +./setup.sh ``` -### Subsequent Runs -Once you already have everything setup for the app, you can simply run `vagrant up`. If you only need the application, and have your own SQL server, redis, and mailhog (or some combination), you can run `vagrant up [boxes]` and replace `[boxes]` with the boxes to bring online. +#### What is Created +* Traefik Container +* Panel & Wings Containers +* MySQL & Redis Containers +* Minio Container for S3 emulation -### Daemons -There are VMs for both, the old and the new, daemons configured. They do not start automatically. You can start them using `vagrant up daemon` (for the nodejs one) and `vagrant up wings` (the golang one). +### Accessing the Environment +Once you've setup the environment, simply run `./beak up -d` to start the environment. This simply aliases +some common Docker compose commands. -### Updating /etc/hosts -On your first run, and whenever the hostnames change, you'll have to run `vagrant hostmanager app --provider docker` to update your /etc/hosts file. +Once the environment is running, `./beak app` and `./beak wings` will allow SSH access to the Panel and +Wings environments respectively. Your Panel is accessible at `https://pterodactyl.test`. You'll need to +run through the normal setup process for the Panel if you do not have a database and environment setup +already. This can be done by SSH'ing into the Panel environment and running `setup-pterodactyl`. -### Configuring the Environment -A `vagrant.config.yml` can be used to configure the following properties of the environment. All values are optional. - -```yml -wings: # the VM used for wings - cpus: 2 - memory: 2048 -``` \ No newline at end of file +The code for the setup can be found in `build/panel/setup-pterodactyl`. Please note, this environment uses +Mutagen for file handling, so replace calls to `docker compse up` or `down` with `mutagen-compose up` or `down`. +All other `docker compose` commands can be used as normal. diff --git a/Vagrantfile b/Vagrantfile deleted file mode 100644 index 6676c85..0000000 --- a/Vagrantfile +++ /dev/null @@ -1,143 +0,0 @@ -require 'yaml' -require 'pathname' - -["vagrant-vbguest", "vagrant-hostmanager"].each do |plugin| - unless Vagrant.has_plugin?(plugin) - raise plugin + " plugin is not installed. Hint: vagrant plugin install " + plugin - end -end - -vagrant_root = File.dirname(__FILE__) - -$configMap = FileTest.exist?("#{vagrant_root}/vagrant.config.yml") ? YAML.load_file("#{vagrant_root}/vagrant.config.yml") : {} -def config(key, def_val) - return $configMap.dig(*key.split(".")) || def_val -end - -Vagrant.configure("2") do |config| - config.hostmanager.enabled = false - config.hostmanager.manage_host = true - config.hostmanager.manage_guest = false - config.hostmanager.ignore_private_ip = false - config.hostmanager.include_offline = true - - config.vm.define "app", primary: true do |app| - app.vm.hostname = "pterodactyl.test" - - app.vm.synced_folder ".", "/vagrant", disabled: true - - app.vm.network "forwarded_port", guest: 80, host: 80 - app.vm.network "forwarded_port", guest: 443, host: 443 - app.vm.network "forwarded_port", guest: 8080, host: 8080 - app.vm.network "forwarded_port", guest: 8081, host: 8081 - - app.ssh.insert_key = true - app.ssh.username = "vagrant" - app.ssh.password = "vagrant" - - app.vm.provider "docker" do |d| - d.image = "ghcr.io/pterodactyl/development/panel" - d.create_args = [ - "-it", - "--add-host=host.pterodactyl.test:172.17.0.1", - "--add-host=wings.pterodactyl.test:192.168.50.3", - ] - d.ports = ["80:80", "443:443", "8080:8080", "8081:8081"] - d.name = "pterodev_app" - - if ENV['FILE_SYNC_METHOD'] === 'docker-sync' - d.volumes = [ - "panel-sync:/home/vagrant/app:nocopy", - "#{vagrant_root}/.data/certificates:/etc/ssl/private:ro" - ] - else - d.volumes = [ - "#{vagrant_root}/code/panel:/home/vagrant/app:cached", - "#{vagrant_root}/.data/certificates:/etc/ssl/private:ro" - ] - end - - d.remains_running = true - d.has_ssh = true - end - - app.vm.provision :hostmanager - app.vm.provision "deploy_nginx_config", type: "file", source: "#{vagrant_root}/build/configs/nginx/pterodactyl.test.conf", destination: "/tmp/.deploy/nginx/pterodactyl.test.conf" - app.vm.provision "deploy_supervisor_config", type: "file", source: "#{vagrant_root}/build/configs/supervisor/pterodactyl.conf", destination: "/tmp/.deploy/supervisor/pterodactyl.conf" - app.vm.provision "configure_application", type: "shell", privileged: false, path: "#{vagrant_root}/scripts/deploy_app.sh" - app.vm.provision "setup", type: "shell", run: "never", inline: <<-SHELL - cd /home/vagrant/app - - cp .env .env.bkup - php artisan key:generate --force --no-interaction - - php artisan p:environment:setup --new-salt --author="you@example.com" --url="http://pterodactyl.test" --timezone="America/Los_Angeles" --cache=redis --session=redis --queue=redis --redis-host="host.pterodactyl.test" --no-interaction - php artisan p:environment:database --host="host.pterodactyl.test" --database=panel --username=pterodactyl --password=pterodactyl --port=33060 --no-interaction - php artisan p:environment:mail --driver=smtp --email="outgoing@example.com" --from="Pterodactyl Panel" --host="host.pterodactyl.test" --port=1025 --no-interaction - - php artisan migrate --seed - SHELL - end - - config.vm.define "wings", autostart: false do |wings| - wings.vm.hostname = "wings.pterodactyl.test" - wings.vm.box = "generic/debian10" - - wings.vm.provider "virtualbox" do |v| - v.memory = config("wings.memory", 2048) - v.cpus = config("wings.cpus", 2) - v.customize ["modifyvm", :id, "--cpuexecutioncap", "75"] - end - - wings.vm.synced_folder ".", "/vagrant", disabled: true - wings.vm.synced_folder "#{vagrant_root}/code/wings", "/home/vagrant/wings", owner: "vagrant", group: "vagrant" - wings.vm.synced_folder "#{vagrant_root}/.data/certificates", "/etc/ssl/pterodactyl", owner: "vagrant", group: "vagrant" - - wings.vm.network :private_network, ip: "192.168.50.3" - - wings.vm.provision "provision", type: "shell", path: "#{vagrant_root}/scripts/provision_wings.sh" - config.vm.provision "file", source: "~/.gitconfig", destination: ".gitconfig" - end - - # Configure a mysql docker container. - config.vm.define "mysql" do |mysql| - mysql.vm.hostname = "mysql.pterodactyl.test" - mysql.vm.synced_folder ".", "/vagrant", disabled: true - - mysql.vm.network "forwarded_port", guest: 3306, host: 33060 - - mysql.vm.provider "docker" do |d| - d.image = "mysql:8" - d.ports = ["33060:3306"] - d.cmd = [ - "--sql-mode=no_engine_substitution", - "--innodb-buffer-pool-size=1G", - "--innodb-log-file-size=256M", - "--innodb-flush-log-at-trx-commit=0", - "--lower-case-table-names=1" - ] - d.volumes = ["#{vagrant_root}/.data/mysql:/var/lib/mysql:cached"] - d.env = { - "MYSQL_ROOT_PASSWORD": "root", - "MYSQL_DATABASE": "panel", - "MYSQL_USER": "pterodactyl", - "MYSQL_PASSWORD": "pterodactyl" - } - d.remains_running = true - end - end - - # Create a docker container for the redis server. - config.vm.define "redis" do |redis| - redis.vm.hostname = "redis.pterodactyl.test" - redis.vm.synced_folder ".", "/vagrant", disabled: true - - redis.vm.network "forwarded_port", guest: 6379, host: 6379 - - redis.vm.provider "docker" do |d| - d.image = "redis:5-alpine" - d.ports = ["6379:6379"] - d.remains_running = true - end - end -end diff --git a/beak b/beak new file mode 100755 index 0000000..943f4a3 --- /dev/null +++ b/beak @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +if [ "$1" == "app" ]; then + if [ "$2" == "" ]; then + docker compose exec -u pterodactyl app bash + elif [ "$2" == "root" ]; then + docker compose exec -u root app bash + else + echo "./beak.sh app [root]" + exit 1 + fi +elif [ "$1" == "wings" ]; then + docker compose exec -u root wings bash +elif [ "$1" == "up" ]; then + shift + mutagen-compose up $@ +elif [ "$1" == "down" ]; then + shift + mutagen-compose down $@ +else + echo "./beak.sh [root]" + exit 1 +fi diff --git a/build/panel/Dockerfile b/build/panel/Dockerfile index 9208ff2..f427069 100644 --- a/build/panel/Dockerfile +++ b/build/panel/Dockerfile @@ -48,14 +48,14 @@ RUN apt -y update \ && rm -rf /var/lib/apt/lists/* COPY entrypoint /usr/local/bin/entrypoint -COPY setup /usr/local/bin/setup +COPY setup-pterodactyl /usr/local/bin/setup-pterodactyl COPY configs/supervisord.conf /etc/supervisor/conf.d/supervisord.conf COPY configs/pterodactyl.conf /etc/nginx/sites-available/pterodactyl.conf RUN useradd -m pterodactyl \ && usermod -a -g www-data -G sudo pterodactyl \ && echo "pterodactyl ALL=(ALL) NOPASSWD:ALL" | tee /etc/sudoers.d/pterodactyl \ - && chmod +x /usr/local/bin/setup \ + && chmod +x /usr/local/bin/setup-pterodactyl \ && chmod +x /usr/local/bin/entrypoint \ && rm -rf /etc/nginx/sites-enabled/* \ && ln -s /etc/nginx/sites-available/pterodactyl.conf /etc/nginx/sites-enabled/ \ @@ -67,6 +67,7 @@ RUN useradd -m pterodactyl \ WORKDIR /var/www/html EXPOSE 80 +EXPOSE 8080 USER pterodactyl ENTRYPOINT ["entrypoint"] diff --git a/build/panel/entrypoint b/build/panel/entrypoint index 1fee639..dde82d3 100644 --- a/build/panel/entrypoint +++ b/build/panel/entrypoint @@ -14,10 +14,16 @@ if [ -d "/etc/php/mods-available"]; then done fi +if [ -d "/etc/certs" ]; then + sudo mkdir -p /usr/local/share/ca-certificates/mkcert + sudo cp /etc/certs/pterodactyl*.pem /usr/local/share/ca-certificates/mkcert/ + sudo update-ca-certificates +fi + cd /var/www/html || exit 1 #sudo chown -R pterodactyl:pterodactyl * #sudo chown -R www-data:pterodactyl storage -#sudo chmod -R 775 storage/* bootstrap/cache +sudo chmod -R 775 storage/* bootstrap/cache if [ $# -gt 0 ]; then sudo su -c "/bin/bash" diff --git a/build/panel/setup b/build/panel/setup-pterodactyl similarity index 85% rename from build/panel/setup rename to build/panel/setup-pterodactyl index 8ab92a1..1396a12 100644 --- a/build/panel/setup +++ b/build/panel/setup-pterodactyl @@ -12,8 +12,6 @@ composer install --no-interaction --prefer-dist --no-scripts --no-progress php artisan config:clear yarn install --no-progress -sudo chown -R pterodactyl:pterodactyl * -sudo chown -R www-data:pterodactyl storage sudo chmod -R 775 storage/* bootstrap/cache sudo supervisorctl reread diff --git a/build/wings/Dockerfile b/build/wings/Dockerfile index 5b26624..9f64ed6 100644 --- a/build/wings/Dockerfile +++ b/build/wings/Dockerfile @@ -11,6 +11,7 @@ RUN apt -y update \ && echo "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null \ && apt -y update \ && apt -y --no-install-recommends install \ + git \ iputils-ping \ tar \ zip \ diff --git a/docker-compose.yml b/docker-compose.yml index 5bdca97..558dfc6 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,7 +5,12 @@ services: image: traefik:2.6 restart: always networks: - - pterodactyl + pterodactyl: + aliases: + - pterodactyl.test + - minio.pterodactyl.test + - s3.minio.pterodactyl.test + - wings.pterodactyl.test ports: - "443:443" - "8080:8080" @@ -13,40 +18,54 @@ services: - no-new-privileges=true volumes: - ./traefik:/etc/traefik:ro - - ./.data/certificates:/etc/certs:ro + - ./docker/certificates:/etc/certs:ro - /var/run/docker.sock:/var/run/docker.sock:ro app: build: context: ./build/panel dockerfile: Dockerfile + stop_grace_period: '1s' networks: pterodactyl: aliases: - pterodactyl.test volumes: - - ./code/panel:/var/www/html:cached - - ./docker/certificates:/etc/ssl/private:ro + - panel_code:/var/www/html + - ./docker/certificates:/etc/certs:ro - ./docker/php:/etc/php/mods-available:ro labels: - "traefik.enable=true" - "traefik.http.routers.app.tls=true" - "traefik.http.routers.app.rule=Host(`pterodactyl.test`)" - "traefik.http.routers.app.entrypoints=https" + - "traefik.http.routers.app.service=app" - "traefik.http.services.app.loadbalancer.server.port=80" + - "traefik.http.routers.hmr.tls=true" + - "traefik.http.routers.hmr.rule=Host(`hmr.pterodactyl.test`)" + - "traefik.http.routers.hmr.entrypoints=https" + - "traefik.http.routers.hmr.service=hmr" + - "traefik.http.services.hmr.loadbalancer.server.port=8080" wings: build: context: ./build/wings dockerfile: Dockerfile + stop_grace_period: '1s' tty: true stdin_open: true networks: - pterodactyl: - aliases: - - wings.pterodactyl.test + - pterodactyl + ports: + - "2022:2022" volumes: - - ./code/wings:/home/root/wings:cached - - wings-data:/var/lib/pterodactyl:delegated + - ./code/wings:/home/root/wings + - wings_data:/var/lib/pterodactyl - /var/run/docker.sock:/var/run/docker.sock:ro + labels: + - "traefik.enable=true" + - "traefik.http.routers.wings.tls=true" + - "traefik.http.routers.wings.rule=Host(`wings.pterodactyl.test`)" + - "traefik.http.routers.wings.entrypoints=https" + - "traefik.http.services.wings.loadbalancer.server.port=8080" mysql: image: mariadb:10.7 restart: unless-stopped @@ -58,7 +77,7 @@ services: - --innodb-flush-log-at-trx-commit=0 - --lower-case-table-names=1 volumes: - - mysql:/var/lib/mysql:delegated + - mysql:/var/lib/mysql - ./docker/mysql:/docker-entrypoint-initdb.d:ro environment: MYSQL_ROOT_PASSWORD: root @@ -87,7 +106,7 @@ services: aliases: - s3.minio.pterodactyl.test volumes: - - minio:/var/lib/minio:delegated + - minio:/var/lib/minio labels: - "traefik.enable=true" - "traefik.http.routers.minio.tls=true" @@ -106,9 +125,25 @@ networks: driver: bridge volumes: + panel_code: mysql: driver: local minio: driver: local - wings-data: + wings_data: driver: local + +x-mutagen: + sync: + defaults: + permissions: + defaultFileMode: 0666 + defaultDirectoryMode: 0777 + code: + alpha: "./code/panel" + beta: "volume://panel_code" + mode: "two-way-resolved" + ignore: + vcs: true + paths: + - ".idea" diff --git a/docker-sync.yml b/docker-sync.yml deleted file mode 100644 index cbde3a4..0000000 --- a/docker-sync.yml +++ /dev/null @@ -1,7 +0,0 @@ -version: "2" -syncs: - panel-sync: - sync_strategy: "native_osx" - src: "./code/panel/" - sync_excludes: [".idea", ".git", ".vagrant", "node_modules"] - host_disk_mount_mode: "cached" \ No newline at end of file diff --git a/scripts/deploy_docs.sh b/scripts/deploy_docs.sh deleted file mode 100644 index f3d7be2..0000000 --- a/scripts/deploy_docs.sh +++ /dev/null @@ -1,26 +0,0 @@ -curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add - -echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list -curl -sL https://deb.nodesource.com/setup_10.x | sudo -E bash - - -# Install dependencies and start supervisor -sudo apt install -y --no-install-recommends nginx nodejs yarn supervisor -sudo /usr/bin/supervisord - -# Copy over deployment specific files. -sudo cp /tmp/.deploy/supervisor/pterodocs.conf /etc/supervisor/conf.d/pterodocs.conf -sudo cp /tmp/.deploy/nginx/pterodocs.test.conf /etc/nginx/sites-available/pterodocs.test.conf - -cd ~/docs -yarn - -sudo rm -f /srv/documentation -sudo ln -s ~/docs /srv/documentation - -# Configure and restart nginx -sudo rm -rfv /var/www -sudo rm -rfv /etc/nginx/sites-enabled/* -sudo ln -s /etc/nginx/sites-available/pterodocs.test.conf /etc/nginx/sites-enabled/pterodocs.test.conf - -sudo supervisorctl reread -sudo supervisorctl update -sudo supervisorctl restart nginx \ No newline at end of file diff --git a/scripts/provision_wings.sh b/scripts/provision_wings.sh deleted file mode 100644 index 2e0b49f..0000000 --- a/scripts/provision_wings.sh +++ /dev/null @@ -1,44 +0,0 @@ -#!/bin/bash -export DEBIAN_FRONTEND=noninteractive - -# Add Docker's GPG key and configure the repository -curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg -echo \ - "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian \ - $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null - -# Perform the installation of the required software. -apt -y update -apt -y --no-install-recommends install iputils-ping tar zip unzip make gcc g++ gdb python docker-ce docker-ce-cli containerd.io - -curl -OL https://golang.org/dl/go1.16.5.linux-amd64.tar.gz -tar xvf go1.16.5.linux-amd64.tar.gz -C /usr/local -chown -R root:root /usr/local/go -rm -rf go1.16.5.linux-amd64.tar.gz - -# Install delve for Go debugging support. -GOBIN=/usr/local/go/bin go get github.com/go-delve/delve/cmd/dlv - -# Configure the vagrant user to have permission to use Docker. -usermod -aG docker vagrant - -# Ensure docker is started and will continue to start up. -systemctl enable docker --now - -# Install ctop for easy container metrics visualization. -curl -fsSL https://github.com/bcicen/ctop/releases/download/v0.7.1/ctop-0.7.1-linux-amd64 -o /usr/local/bin/ctop -chmod +x /usr/local/bin/ctop - -# create config directory -mkdir -p /etc/pterodactyl /var/log/pterodactyl -cp /etc/ssl/pterodactyl/root_ca.pem /etc/ssl/certs/mkcert.pem - -# ensure permissions are set correctly -chown -R vagrant:vagrant /home/vagrant /etc/pterodactyl /var/log/pterodactyl - -# map pterodactyl.test to the host system -echo "$(ip route | grep default | cut -d' ' -f3,3) pterodactyl.test" >> /etc/hosts - -cat >> /etc/profile < /dev/null && pwd) +CERTS_DIR="$SCRIPT_DIR/docker/certificates" +CODE_DIR="$SCRIPT_DIR/code" -CURRENT_DIRECTORY="$(pwd)" -cd /tmp - -cd ${CURRENT_DIRECTORY} - -git clone https://github.com/pterodactyl/panel.git code/panel -git clone https://github.com/pterodactyl/documentation.git code/documentation -git clone https://github.com/pterodactyl/wings.git code/wings +for REPO in "panel" "documentation" "wings" +do + if [ ! -d "$CODE_DIR/$REPO" ]; then + git clone https://github.com/pterodactyl/$REPO.git "$CODE_DIR/$REPO" + else + echo "$REPO repository already cloned into: $CODE_DIR/$REPO" + fi +done mkcert -install -mkcert pterodactyl.test '*.pterodactyl.test' +mkcert pterodactyl.test hmr.pterodactyl.test minio.pterodactyl.test s3.minio.pterodactyl.test -mv *pterodactyl.test*-key.pem docker/certificates/pterodactyl.test-key.pem -mv *pterodactyl.test*.pem docker/certificates/pterodactyl.test.pem -cp "$(mkcert -CAROOT)/rootCA.pem" docker/certificates/root_ca.pem +mv -v *pterodactyl.test*-key.pem docker/certificates/pterodactyl.test-key.pem || exit 1 +mv -v *pterodactyl.test*.pem docker/certificates/pterodactyl.test.pem || exit 1 +cp -v "$(mkcert -CAROOT)/rootCA.pem" docker/certificates/root_ca.pem || exit 1 + +echo "" +if [ ! -f "/etc/hosts" ]; then + echo "no system hosts file found, please manually configure your system" +else + for DOMAIN in "pterodactyl.test" "hmr.pterodactyl.test" "minio.pterodactyl.test" "s3.minio.pterodactyl.test" + do + ESCAPED_DOMAIN=$(echo $DOMAIN | sed "s/\./\\\./g") + if ! grep -q -E "127\.0\.0\.1\s+$ESCAPED_DOMAIN\s*$" /etc/hosts; then + echo "✅ adding \"$DOMAIN\" to system hosts file" + echo "127.0.0.1 $DOMAIN" | sudo tee -a /etc/hosts || exit 1 + else + echo "✅ found existing entry for \"$DOMAIN\" in /etc/hosts; skipping..." + fi + done +fi + +echo "optionally, configure the beak alias:" + +echo "bash:" +echo "echo \"alias beak=\\\"$SCRIPT_DIR/beak\\\"\" >> ~/.bash_profile" +echo "" +echo "zsh:" +echo "echo \"alias beak=\\\"$SCRIPT_DIR/beak\\\"\" >> ~/.zshrc"