diff --git a/.agents/resume b/.agents/resume new file mode 100755 index 0000000000..f5d66794e4 --- /dev/null +++ b/.agents/resume @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +set -euo pipefail + +# Long-running preview services are supervised through `.amp/services.yaml`. diff --git a/.agents/setup b/.agents/setup new file mode 100755 index 0000000000..4abfa15cd2 --- /dev/null +++ b/.agents/setup @@ -0,0 +1,271 @@ +#!/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; 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_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 + + php "$demo_root/artisan" storage:link + printf '%s\n' "${current_demo_branch:-detached HEAD}" > "$demo_state_branch_file" +} + +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 'Preparing the matching Filament demo checkout' prepare_demo_checkout +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" diff --git a/.amp/services.yaml b/.amp/services.yaml new file mode 100644 index 0000000000..c4285cf29b --- /dev/null +++ b/.amp/services.yaml @@ -0,0 +1,13 @@ +services: + demo: + command: >- + php artisan optimize:clear && + APP_URL="$PUBLIC_URL" ASSET_URL="$PUBLIC_URL" DEBUGBAR_ENABLED=false + php artisan serve --host=0.0.0.0 --port="$PORT" + cwd: ../repos/demo + health: /login + portal: + url: /login + title: Filament demo + description: Sign in with admin@filamentphp.com and password "demo.Filament@2021!". + agent: control diff --git a/.gitignore b/.gitignore index 0081107459..53aab9093c 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,4 @@ phpunit.xml .env.testing !phpunit.*.xml /tests/**/Screenshots +/.amp/portals/