#!/usr/bin/env bash set -euo pipefail export DEBIAN_FRONTEND=noninteractive repository_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" workspace_root="$(dirname "$repository_root")" demo_root="$workspace_root/repos/demo" demo_state_branch_file="$demo_root/.orb-demo-state-branch" run_step() { local description="$1" shift printf '\n==> %s\n' "$description" time "$@" } install_php() { local php_packages=( php8.4-bcmath php8.4-cli php8.4-curl php8.4-gd php8.4-intl php8.4-mbstring php8.4-mysql php8.4-pgsql php8.4-sqlite3 php8.4-xml php8.4-zip ) local missing_php_packages=() local php_package for php_package in "${php_packages[@]}"; do if ! dpkg-query -W -f='${Status}' "$php_package" 2>/dev/null | grep -Fq 'install ok installed'; then missing_php_packages+=("$php_package") fi done if (( ${#missing_php_packages[@]} > 0 )); then sudo apt-get update sudo apt-get install -y --no-install-recommends apt-transport-https ca-certificates curl lsb-release if ! dpkg-query -W -f='${Status}' debsuryorg-archive-keyring 2>/dev/null | grep -Fq 'install ok installed'; then curl -fsSL https://packages.sury.org/debsuryorg-archive-keyring.deb -o /tmp/debsuryorg-archive-keyring.deb sudo dpkg -i /tmp/debsuryorg-archive-keyring.deb rm /tmp/debsuryorg-archive-keyring.deb fi if [[ ! -f /etc/apt/sources.list.d/php.sources ]]; then sudo tee /etc/apt/sources.list.d/php.sources >/dev/null </dev/null; then local composer_version composer_version="$(composer --version 2>/dev/null | sed -nE 's/^Composer version ([^ ]+).*/\1/p')" if php -r 'exit(version_compare($argv[1], "2.8.0", ">=") ? 0 : 1);' "$composer_version"; then return fi fi local expected_checksum local actual_checksum expected_checksum="$(curl -fsSL https://composer.github.io/installer.sig)" curl -fsSL https://getcomposer.org/installer -o /tmp/composer-setup.php actual_checksum="$(php -r "echo hash_file('sha384', '/tmp/composer-setup.php');")" if [[ "$expected_checksum" != "$actual_checksum" ]]; then echo 'Composer installer checksum verification failed.' >&2 rm /tmp/composer-setup.php return 1 fi sudo php /tmp/composer-setup.php --quiet --install-dir=/usr/local/bin --filename=composer rm /tmp/composer-setup.php } prepare_demo_checkout() { local current_demo_branch local demo_branch local demo_filament_constraint local demo_major_version local filament_major_version local locked_filament_version locked_filament_version="$(php -r ' $lock = json_decode(file_get_contents($argv[1]), true, flags: JSON_THROW_ON_ERROR); foreach (array_merge($lock["packages"], $lock["packages-dev"]) as $package) { if ($package["name"] === "filament/support") { echo $package["version"]; break; } } ' "$repository_root/composer.lock")" filament_major_version="$(sed -E 's/^([0-9]+)\.x-dev$/\1/' <<< "$locked_filament_version")" demo_major_version="$filament_major_version" # Filament 4.x feature work currently uses the 5.x demo application. if [[ "$filament_major_version" == 4 ]]; then demo_major_version=5 fi demo_branch="$demo_major_version.x" if ! git ls-remote --exit-code --heads https://github.com/filamentphp/demo.git "$demo_branch" >/dev/null 2>&1; then echo "The preferred filamentphp/demo branch $demo_branch was not found for Filament $locked_filament_version." >&2 return 1 fi if [[ ! -d "$demo_root/.git" ]]; then echo 'The filamentphp/demo additional repository was not found at ../repos/demo.' >&2 echo 'Add it under “Additional Repositories to Clone” in the Amp project settings.' >&2 return 1 fi current_demo_branch="$(git -C "$demo_root" branch --show-current)" demo_filament_constraint="$(php -r ' $manifest = json_decode(file_get_contents($argv[1]), true, flags: JSON_THROW_ON_ERROR); echo $manifest["require"]["filament/filament"] ?? ""; ' "$demo_root/composer.json")" if [[ ! "$demo_filament_constraint" =~ (^|[^0-9])$demo_major_version([^0-9]|$) ]]; then if [[ -n "$(git -C "$demo_root" status --porcelain)" ]]; then echo "The existing demo checkout requires Filament $demo_filament_constraint and has changes." >&2 echo "Preserve or remove those changes before switching it to $demo_branch." >&2 return 1 fi if [[ ! -f "$demo_state_branch_file" ]]; then printf '%s\n' "${current_demo_branch:-detached HEAD}" > "$demo_state_branch_file" fi git -C "$demo_root" fetch --depth=1 origin "$demo_branch" git -C "$demo_root" checkout -B "$demo_branch" FETCH_HEAD fi current_demo_branch="$(git -C "$demo_root" branch --show-current)" printf 'Using filamentphp/demo %s at %s.\n' "${current_demo_branch:-detached HEAD}" "$(git -C "$demo_root" rev-parse --short HEAD)" mkdir -p "$demo_root/.git/info" for excluded_path in composer.orb.json composer.orb.lock .composer-orb-hash .orb-demo-state-branch config/trustedproxy.php; do if ! grep -Fqx "/$excluded_path" "$demo_root/.git/info/exclude" 2>/dev/null; then printf '/%s\n' "$excluded_path" >> "$demo_root/.git/info/exclude" fi done } write_demo_proxy_configuration() { if [[ -e "$demo_root/config/trustedproxy.php" ]]; then return fi cat > "$demo_root/config/trustedproxy.php" <<'PHP' 'REMOTE_ADDR', ]; PHP } write_demo_composer_manifest() { php -r ' $manifest = json_decode(file_get_contents($argv[1]), true, flags: JSON_THROW_ON_ERROR); foreach (array_keys($manifest["require"]) as $package) { if (str_starts_with($package, "filament/")) { $manifest["require"][$package] = "*"; } } $manifest["repositories"] = [[ "type" => "path", "url" => $argv[3] . "/packages/*", "options" => ["symlink" => true], ]]; $manifest["minimum-stability"] = "dev"; $manifest["prefer-stable"] = true; file_put_contents( $argv[2], json_encode($manifest, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR) . PHP_EOL, ); ' "$demo_root/composer.json" "$demo_root/composer.orb.json" "$repository_root" } install_demo_dependencies() { local composer_hash local previous_composer_hash='' write_demo_composer_manifest composer_hash="$({ sha256sum "$demo_root/composer.json" "$demo_root/composer.lock"; find "$repository_root/packages" -mindepth 2 -maxdepth 2 -name composer.json -print0 | sort -z | xargs -0 sha256sum; } | sha256sum | cut -d ' ' -f 1)" if [[ -f "$demo_root/.composer-orb-hash" ]]; then previous_composer_hash="$(cat "$demo_root/.composer-orb-hash")" fi if [[ ! -f "$demo_root/composer.orb.lock" ]] || [[ "$composer_hash" != "$previous_composer_hash" ]]; then cp "$demo_root/composer.lock" "$demo_root/composer.orb.lock" COMPOSER=composer.orb.json composer --working-dir="$demo_root" update 'filament/*' --with-dependencies --minimal-changes --prefer-dist --no-interaction printf '%s\n' "$composer_hash" > "$demo_root/.composer-orb-hash" else COMPOSER=composer.orb.json composer --working-dir="$demo_root" install --prefer-dist --no-interaction fi npm --prefix "$demo_root" ci --ignore-scripts npm --prefix "$demo_root" run build } initialize_demo_application() { local current_demo_branch local database_was_created=false local previous_demo_branch='' local preserved_database_path if [[ ! -f "$demo_root/.env" ]]; then cp "$demo_root/.env.example" "$demo_root/.env" fi current_demo_branch="$(git -C "$demo_root" branch --show-current)" if [[ -f "$demo_state_branch_file" ]]; then previous_demo_branch="$(cat "$demo_state_branch_file")" fi if [[ -s "$demo_root/database/database.sqlite" ]] && [[ -n "$previous_demo_branch" ]] && [[ "$previous_demo_branch" != "${current_demo_branch:-detached HEAD}" ]]; then preserved_database_path="$demo_root/database/database.sqlite.before-${previous_demo_branch//\//-}-$(date +%s).bak" mv "$demo_root/database/database.sqlite" "$preserved_database_path" printf 'Preserved the %s demo database at %s.\n' "$previous_demo_branch" "$preserved_database_path" fi if [[ ! -s "$demo_root/database/database.sqlite" ]]; then mkdir -p "$demo_root/database" touch "$demo_root/database/database.sqlite" database_was_created=true fi if ! grep -Eq '^APP_KEY=base64:.+' "$demo_root/.env"; then php "$demo_root/artisan" key:generate --force fi if [[ "$database_was_created" == true ]]; then php "$demo_root/artisan" migrate --force php "$demo_root/artisan" db:seed --force else php "$demo_root/artisan" migrate --force fi if [[ ! -L "$demo_root/public/storage" ]]; then php "$demo_root/artisan" storage:link fi printf '%s\n' "${current_demo_branch:-detached HEAD}" > "$demo_state_branch_file" } initialize_docs_screenshot_application() { local docs_application_root="$repository_root/docs-assets/app" if [[ ! -f "$docs_application_root/.env" ]]; then cp "$docs_application_root/.env.example" "$docs_application_root/.env" fi mkdir -p "$docs_application_root/database" if [[ ! -f "$docs_application_root/database/database.sqlite" ]]; then touch "$docs_application_root/database/database.sqlite" fi if ! grep -Eq '^APP_KEY=base64:.+' "$docs_application_root/.env"; then php "$docs_application_root/artisan" key:generate --force fi php "$docs_application_root/artisan" migrate:fresh --seed --force php "$docs_application_root/artisan" storage:link --force } cd "$repository_root" run_step 'Installing PHP 8.4 and extensions' install_php run_step 'Installing Composer' install_composer run_step 'Installing Filament Composer dependencies' composer install --prefer-dist --no-interaction --no-scripts run_step 'Installing Filament Node.js dependencies' npm ci --ignore-scripts run_step 'Installing Playwright Chromium' npx playwright install --with-deps chromium run_step 'Building Filament assets' npm run build run_step 'Installing docs screenshot application Composer dependencies' composer --working-dir="$repository_root/docs-assets/app" install --prefer-dist --no-interaction run_step 'Installing docs screenshot application Node.js dependencies' npm --prefix "$repository_root/docs-assets/app" ci --ignore-scripts run_step 'Installing docs screenshot generator dependencies' npm --prefix "$repository_root/docs-assets/screenshots" ci run_step 'Initializing the docs screenshot application' initialize_docs_screenshot_application run_step 'Building the docs screenshot application assets' npm --prefix "$repository_root/docs-assets/app" run build run_step 'Preparing the matching Filament demo checkout' prepare_demo_checkout run_step 'Configuring the demo portal proxy' write_demo_proxy_configuration run_step 'Installing locally linked demo dependencies' install_demo_dependencies run_step 'Initializing the demo Laravel application' initialize_demo_application printf '\nOrb setup complete. The demo checkout is at %s.\n' "$demo_root" printf 'Docs screenshot generation is ready in %s.\n' "$repository_root/docs-assets/screenshots"