diff --git a/.env.example b/.env.example index e4518aa..99e24cb 100644 --- a/.env.example +++ b/.env.example @@ -3,7 +3,7 @@ APP_ENV=local APP_KEY= APP_DEBUG=true APP_TIMEZONE=UTC -APP_URL=https://api.aspiredev.org +APP_URL=https://api.aspirecloud.localhost # Feature Flags @@ -61,12 +61,12 @@ REDIS_PASSWORD=null REDIS_PORT=6379 MAIL_MAILER=smtp -MAIL_HOST=mail.aspiredev.org +MAIL_HOST=mail.aspirecloud.localhost MAIL_PORT=1025 MAIL_USERNAME=null MAIL_PASSWORD=null MAIL_ENCRYPTION=null -MAIL_FROM_ADDRESS="do-not-reply@aspiredev.org" +MAIL_FROM_ADDRESS="do-not-reply@aspirecloud.localhost" MAIL_FROM_NAME="${APP_NAME}" AWS_ACCESS_KEY_ID= @@ -78,13 +78,13 @@ AWS_USE_PATH_STYLE_ENDPOINT=false VITE_APP_NAME="${APP_NAME}" # Services configuration -MAILGUN_DOMAIN=aspiredev.org +MAILGUN_DOMAIN=aspirecloud.localhost MAILGUN_ENDPOINT=api.mailgun.net MAILGUN_SECRET= # AspirePress Related Configuration -DOWNLOAD_BASE=https://api.aspiredev.org/download/ -# DOWNLOAD_BASE=https://fastly.api.aspiredev.org/download/ +DOWNLOAD_BASE=https://api.aspirecloud.localhost/download/ +# DOWNLOAD_BASE=https://fastly.api.aspirecloud.localhost/download/ DOWNLOAD_CACHE_SECONDS=864000 # Elasticsearch Configuration diff --git a/Makefile b/Makefile deleted file mode 100644 index 8415376..0000000 --- a/Makefile +++ /dev/null @@ -1,157 +0,0 @@ -.PHONY: * - -# Set additional parameters for command -OPTS= - -# Set DEBUG=1 to enable xdebug -ifeq ($(origin DEBUG),undefined) - XDEBUG := - PHPSTAN_XDEBUG := -else - XDEBUG := XDEBUG_SESSION=1 - PHPSTAN_XDEBUG := --xdebug -endif - -ifneq (,$(wildcard ./.env)) - include .env - export -endif - -list: - @grep -E '^[a-zA-Z%_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | perl -ne '/^(?:.*?:)?(.*?):.*##(.*$$)/ and printf "\033[36m%-30s\033[0m %s\n", $$1, $$2' - -init: check-env dirs down clean build-containers network up build reset-database reset-testing-database generate-key ## Initial configuration tasks - -dirs: - mkdir -p .cache - -check-env: - @[ -f .env ] || { cp .env.example .env; } - -build-containers: ## Builds the Docker containers - docker compose build - -clean: ## Remove all Docker containers, volumes, etc - docker compose down -v --remove-orphans - docker compose rm -f - rm -fr ./vendor ./node_modules - -up: ## Starts the Docker containers - docker compose up -d - -build: install-php install-js build-js - -down: ## Stops the Docker containers - docker compose down - -unit: ## Run unit tests - bin/dcrun vendor/bin/pest --testsuite=Unit ${OPTS} - -functional: reset-testing-database ## Run functional tests - bin/dcrun vendor/bin/pest --testsuite=Feature ${OPTS} - -test-bruno: ## Run bruno tests (yarn global add @usebruno/cli) - cd bruno && bru run -r . --env 'Local API' - -test: unit functional ## Run all tests - -acceptance: ## Run acceptance tests - bin/dcrun vendor/bin/behat -vvv ${OPTS} - -quality: ## Run all quality checks - bin/dcrun vendor/bin/phpstan --memory-limit=1G analyse ${OPTS} - -quality-baseline: ## Run all static analysis checks with baseline - bin/dcrun vendor/bin/phpstan analyse -b baseline.neon $(PHPSTAN_XDEBUG) src tests - -install-php: ## Install composer dependencies - bin/dcrun composer install - -install-js: - bin/dcrun yarn - -build-js: - bin/dcrun yarn run build - -logs-%: ## View logs (follow mode) for the container where % is a service name (webapp, postgres, node, nginx, smtp, rabbitmq) - docker compose logs -f $* - -sh-webapp: # webapp is alpine, so we need to use sh, not sh - docker compose exec webapp sh - -sh-%: ## Execute shell for the container where % is a service name (webapp, postgres, node, nginx, smtp, rabbitmq) - docker compose exec $* sh || docker compose run --rm $* sh - -clear-cache: ## Clear cache - bin/dcrun php artisan optimize:clear - -helpers: ## Generate Laravel IDE helpers - @if [ -z $(FORCE) ]; then \ - echo "*** laravel-ide-helper generates incorrect code for Laravel 11, and is not recommended at this time."; \ - echo "*** if you wish to generate helpers anyway, run 'make helpers FORCE=1'"; \ - exit 1; \ - fi - bin/dcrun php artisan ide-helper:generate - bin/dcrun php artisan ide-helper:meta - bin/dcrun php artisan ide-helper:models --write --smart-reset - -lint: style quality ## Check code standards conformance - -check: lint test ## Run lint and unit tests - -fix: fix-style ## Run automated code fixes - -style: ## Run code style checks - bin/dcrun vendor/bin/pint --test - -fix-style: ## Run code style fixes - bin/dcrun vendor/bin/pint - -create-migration: ## Create a new database migration - bin/dcrun php artisan make:migration - -create-seed: ## Create a new database seed - bin/dcrun php artisan make:seed - -migrate: ## Run database migrations - bin/dcrun php artisan migrate --force --no-interaction - -migration-rollback: ## Rollback database migrations - bin/dcrun php artisan migrate --force --no-interaction - -seed: ## Run database seeds - bin/dcrun php artisan db:seed - -generate-key: ## Generate APP_KEY environment var - bin/dcrun php artisan key:generate - -drop-database: - bin/dcrun sh -c "export PGPASSWORD=${DB_ROOT_PASSWORD} && psql -U ${DB_ROOT_USERNAME} -h ${DB_HOST} -c 'drop database if exists ${DB_DATABASE}'" - -create-database: - bin/dcrun sh -c "export PGPASSWORD=${DB_ROOT_PASSWORD} && psql -U ${DB_ROOT_USERNAME} -h ${DB_HOST} -c 'create database ${DB_DATABASE} owner ${DB_USERNAME}'" - -reset-database: drop-database create-database migrate seed ## run migrations and seeds - -reset-testing-database: - bin/dcrun meta/bin/reset-testing-database - -run-psql: ## Runs Postgres on the command line using the .env file variables - bin/dcrun sh -c "PGPASSWORD=${DB_PASSWORD} psql -U ${DB_USERNAME} -h ${DB_HOST} -p ${DB_PORT} -d ${DB_USERNAME}" - -network: ## Create docker networks for aspire-net and traefik proxy (if they don't exist already) - bin/create-external-network.sh aspire-net - bin/create-external-network.sh traefik - -rm-network: ## Remove aspire-net docker network. (traefik is not touched) - -bin/remove-external-network.sh aspire-net - -build-prod: - docker build --target prod -t aspirepress/aspirecloud-php -f ./docker/webapp/Dockerfile . - -traefik-up: network - docker compose -f docker/traefik/docker-compose.yml up -d - -traefik-down: - docker compose -f docker/traefik/docker-compose.yml down - diff --git a/README.md b/README.md index d8b8a61..9b5b66a 100644 --- a/README.md +++ b/README.md @@ -1,27 +1,15 @@ [//]: # (@formatter:off) # AspireCloud -This project is designed to function as a CDN/API endpoint system for distributing WordPress assets (themes, plugins, core) to users of the [AspirePress Updater](https://github.com/aspirepress/updater-plugin). +This project is designed to function as an API endpoint for distributing [FAIR](https://fair.pm) packages. Currently it can serve WordPress plugins through [FAIR Connect](https://github.com/fairpm/fair-plugin) as well as extensions for TYPO3 v14.0 and up. -# 🪧 CloudFest Hackathon 2025: see [/docs/readme.hackathon.md](./docs/readme.hackathon.md) +## Quick Start -## Setup +If you're new to FAIR, we recommend using the [start-here repo](https://github.com/fairpm/start-here#quick-start), which will download and bootstrap an AspireCloud instance with just one command (`just start`). After that, your instance will be accessible at https://api.aspirecloud.localhost. -### Quick Start +### Quick Start for Standalone instance -``` -make init -``` - -Next configure WordPress to use your local version of AspireCloud, and you're good to go! - -## Using https://api.aspiredev.org instead of localhost - -The local dev instance can be reached this way by enabling a [Traefik](https://hub.docker.com/_/traefik) proxy server: - - make traefik-up - -You will then be able to reach the instance at https://api.aspiredev.org +If you're not running the whole start-here stack, you can still bring up an AC instance with `just start` in a fresh checkout of the AspireCloud repo (this one). Since none of the services expose local ports by default, you will want to edit `docker-compose.override.yml` to add listening ports (see docker-compose.override.yml.dist for examples). ## Notes diff --git a/bin/create-external-network.sh b/bin/create-external-network.sh deleted file mode 100755 index 67a62cb..0000000 --- a/bin/create-external-network.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/env bash - -# Create network if it doesn't exist already -network_name=${1?no network name specified} - -if ! docker network inspect "$network_name" &> /dev/null; then - docker network create "$network_name" - echo "Created network $network_name" -fi - diff --git a/bin/dcexec b/bin/dcexec index 4bbf7bd..0a655a1 100755 --- a/bin/dcexec +++ b/bin/dcexec @@ -1,8 +1,4 @@ #!/bin/bash - cd $(dirname $0)/.. -base=$(pwd) - -svc=${APP_SERVICE:-webapp} -exec docker compose exec $svc "$@" +exec docker compose exec "${DCEXEC_SERVICE:-webapp}" "$@" diff --git a/bin/dcrun b/bin/dcrun index 2e5694f..e023ddc 100755 --- a/bin/dcrun +++ b/bin/dcrun @@ -1,8 +1,4 @@ #!/bin/bash - cd $(dirname $0)/.. -base=$(pwd) - -svc=${CLI_SERVICE:-cli} -exec docker compose run --rm $svc "$@" +exec docker compose run --rm "${DCRUN_SERVICE:-cli}" "$@" diff --git a/bin/remove-external-network.sh b/bin/remove-external-network.sh deleted file mode 100755 index f17f89e..0000000 --- a/bin/remove-external-network.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env bash - -set -o errexit - -# Remove network if it exists -network_name=${1?no network name specified} - -if docker network inspect "$network_name" &> /dev/null; then - docker network rm "$network_name" - echo "Removed network $network_name" -fi diff --git a/bruno/environments/Local API.bru b/bruno/environments/Local API.bru index 2f9bbd3..79a4b90 100644 --- a/bruno/environments/Local API.bru +++ b/bruno/environments/Local API.bru @@ -1,6 +1,6 @@ vars { - API_URL: http://api.aspiredev.org - DOWNLOADS_URL: http://downloads.aspiredev.org + API_URL: http://api.aspirecloud.localhost + DOWNLOADS_URL: http://downloads.aspirecloud.localhost } vars:secret [ API_KEY diff --git a/docker-compose.yml b/docker-compose.yml index d04c6bb..6d28802 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -13,11 +13,11 @@ services: app-net: ~ aspire-net: aliases: - - 'api.aspiredev.org' + - 'api.aspirecloud.localhost' labels: - "traefik.enable=true" - - "traefik.http.routers.ap-api.rule=Host(`api.aspiredev.org`)" - - "traefik.http.routers.ap-api-https.rule=Host(`api.aspiredev.org`)" + - "traefik.http.routers.ap-api.rule=Host(`api.aspirecloud.localhost`)" + - "traefik.http.routers.ap-api-https.rule=Host(`api.aspirecloud.localhost`)" - "traefik.http.routers.ap-api-https.tls=true" redis: @@ -37,7 +37,7 @@ services: app-net: ~ aspire-net: aliases: - - 'db.aspiredev.org' + - 'db.aspirecloud.localhost' queue-worker: build: @@ -86,7 +86,7 @@ services: app-net: ~ aspire-net: aliases: - - 'mail.aspiredev.org' + - 'mail.aspirecloud.localhost' environment: MP_MAX_MESSAGES: 500 MP_SMTP_AUTH_ACCEPT_ANY: 1 diff --git a/docker/traefik/README.md b/docker/traefik/README.md deleted file mode 100644 index a26905d..0000000 --- a/docker/traefik/README.md +++ /dev/null @@ -1,42 +0,0 @@ -# Docker Network Proxy - -## AspirePress Users -The steps below are of interest to anyone wanting to use the proxy with their own services. -If you are building AspireCloud using the Makefile, all the below steps have already been performed, -and you only need edit your `/etc/hosts` file as described in the project README. - ----- - -## How to proxy your own services - -The proxy depends on an external network being defined, so run the following in your shell (you only ever need to do this once) - - docker network create traefik - -### Enabling the proxy in docker-compose.yml - -Add the following to the service you want proxied, substituting `myservice` and `myhostname` appropriately (`myservice` can be anything you want, but it must be unique across all your docker containers) - - labels: - - "traefik.enable=true" - - "traefik.http.routers.myservice.rule=Host(`myhostname.local`)" - - "traefik.http.routers.myservice-https.rule=Host(`myhostname.local`)" - - "traefik.http.routers.myservice-https.tls=true" - networks: - - traefik - -Add the following to the top level keys - - networks: - traefik: - external: true - -### Add an entry to your hosts file - -Add the following lines to your `/etc/hosts` file (`C:\Windows\System32\drivers\etc\hosts` on Windows) - -``` -127.0.0.1 myhostname.local -::1 myhostname.local -``` - diff --git a/docker/traefik/certs/_wildcard.local-key.pem b/docker/traefik/certs/_wildcard.local-key.pem deleted file mode 100644 index e0727d7..0000000 --- a/docker/traefik/certs/_wildcard.local-key.pem +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDVAxQ/RLORE2JV -y6x45+OMJfeauaQirHJYEVZE2QlF+vMKcm77RrAUM9oHHq68rrpIY3Dn6VatyL2i -fTdWHasY8hHsYTJvzrInEKaSZLEAsp0tvFIWIT1R1sTDF/TxDpjfvkl5Y++bf+g2 -ChgDCDst38Y/PRw745byRUUFmaZVszBXXFFadGLs5HwFo4rRwj8rxt/oTcTw8kBx -fe/hYdckhsjfgvyc8E/5vaPGGa/d0afACiv2+Ztqzmb4TI0dovlXKsfIo+dlY2C5 -OmVbdufj7EclOfZOibd1GfZLzmwPDdgrF1sU7hg68eAopIf541J5B8oFR+7cGI/9 -Ygp1np4HAgMBAAECggEAe5Mht+KfWdapAaT7FdlRRQjxsC1qb0MjwrusFJzkqFiB -jtn8UIIdvFqobeD69jNXlo85/RB9XKxMd2u1Ipxrmjk09LEelYaQC+G5AMyxcvor -/VB3Pqdh74xs8dGAJCiXv5WhAm9bnhaCHHeWnouz6VUcqatGKehKtZpCBJGy0Zvm -8cK4fkkOeiaEquuI/QS/Inbn1yzkL8qAbVHGcjRXCLxHPe0y8lpx8z79ciB2Xx9d -34cx1FDRPb11kVLW4wSQfuVvjwE4XcGd3D1LIeRaFJstAqkH57WT2XhiPsvtBcK2 -bzPi0jxi5y6RozxlgbLRc2babUI5U/ISGuvcDU4fAQKBgQD8qOQnpskcAraOHjqQ -OOVAU640p2coZYFy/UjTvIlhIJpMYYkfhvCiptUvGgrjY0JGWzSBEzOJ93+tC7XQ -iqejNm7uKyy7f5hgImdN7YS8lFUdh0loVx27CaTtKYZt2+n2yPqEUX6Uzg3w5RG5 -bc32OiBHmYtmW1676UBVsH0hpwKBgQDX1ADHoZ0GMxuYhBskgF6LCWW1lPRTJas/ -AWdC2NknJc9z1OnVHeF7nug8UgHeEaTCOeVcRmmD5jaZShI5UPH+b+Cf0EGbB+dr -XeS8q4uwRgw0Df60QrCrKzFoiWu1JUV2+/djXgmXK5gqgTydmHGO8itsM0DPx8ea -uy9eQLZsoQKBgHVgSM4a76bxo7mWVYKWy/n8ZhvUAVqE/YRcIq8rEW9r5ALD9R4O -ZFBLzRIauSA2Bp4E1Pl54CGiN1A+9nIxV/u82AARUrCwcCuJEBDl3Rp2lqwnI3Uz -s34m1tXOysLSvXiGmctKyNF8gIF/CaLpXhubmU9qB5a/r90AWYD1gsxVAoGAQrsK -zq56ZVtR8fMOjVefY6+xIdZ/QGIKM33UloeDhKYdITw4XCtT9yvntBbK+wVLgr3x -23jt0NlSEk0jm1Mar0eaFO+ICU4FUF4Ehg0oMIVH6qRVFxjzlMjTCvZKNgTjGGnJ -UJDXuETDVIkG29cxbTgVEuw54sRrQ70SZTru7GECgYBh9acJ7r+KFyRbcjCRdg9a -VXNvMFrwUxH5EvYKpuZle85JxF3PIa+V3HP0ZyRhSppL/q4IDhEyQ5UlGDwRIj1y -1fXgWch6oFaI8zrEbbk4d1OPVY0moRDcqAhsH1hOuje9FKdiLKFCWtpSIgSXOXpd -BUd2+3PXBzPJWPl3gJNJWg== ------END PRIVATE KEY----- diff --git a/docker/traefik/certs/_wildcard.local.pem b/docker/traefik/certs/_wildcard.local.pem deleted file mode 100644 index 3626a1f..0000000 --- a/docker/traefik/certs/_wildcard.local.pem +++ /dev/null @@ -1,25 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIEIzCCAougAwIBAgIRAOxjzCCMmHqkRSGahdFRyQYwDQYJKoZIhvcNAQELBQAw -bzEeMBwGA1UEChMVbWtjZXJ0IGRldmVsb3BtZW50IENBMSIwIAYDVQQLDBljaHVj -a0B0cmFuZSAoQ2h1Y2sgQWRhbXMpMSkwJwYDVQQDDCBta2NlcnQgY2h1Y2tAdHJh -bmUgKENodWNrIEFkYW1zKTAeFw0yMDExMTAyMjIzNDhaFw0yMzAyMTAyMjIzNDha -ME0xJzAlBgNVBAoTHm1rY2VydCBkZXZlbG9wbWVudCBjZXJ0aWZpY2F0ZTEiMCAG -A1UECwwZY2h1Y2tAdHJhbmUgKENodWNrIEFkYW1zKTCCASIwDQYJKoZIhvcNAQEB -BQADggEPADCCAQoCggEBANUDFD9Es5ETYlXLrHjn44wl95q5pCKsclgRVkTZCUX6 -8wpybvtGsBQz2gcerryuukhjcOfpVq3IvaJ9N1YdqxjyEexhMm/OsicQppJksQCy -nS28UhYhPVHWxMMX9PEOmN++SXlj75t/6DYKGAMIOy3fxj89HDvjlvJFRQWZplWz -MFdcUVp0YuzkfAWjitHCPyvG3+hNxPDyQHF97+Fh1ySGyN+C/JzwT/m9o8YZr93R -p8AKK/b5m2rOZvhMjR2i+Vcqx8ij52VjYLk6ZVt25+PsRyU59k6Jt3UZ9kvObA8N -2CsXWxTuGDrx4Cikh/njUnkHygVH7twYj/1iCnWengcCAwEAAaNcMFowDgYDVR0P -AQH/BAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMB8GA1UdIwQYMBaAFKdilAYi -FX9alk/Ds2jcWDkj6baLMBIGA1UdEQQLMAmCByoubG9jYWwwDQYJKoZIhvcNAQEL -BQADggGBADAWoUOYl0PF2BHrK1ODcNFbChuVwg847YN7ak0l2R4vQDhp1GNLBlJq -T8Q5b10syZzZDapzB7dU6lUtnBtVvcMuxgN2eJgklthxeDxvfab/nhug2E1p+To5 -tv+c3mK3HR54gQgd6j+Fjr2TqXQh0b7BK5wGn4gUu+czLkr+TgMJ9S36k2GbU5hr -q0MZpte4yAA4u03Ucxvr3WsKXiVHJd2gRtyC6vcw8E6hjlKuTymaKGK2qwHBSX96 -DHbH4Q1hei0BWjVrJGZVK7ef37EOVK25QNizoAvcRxsj1OXkh+9rHXZY6HDCsweb -an6LAZzdhWraHjvvax4jMsM/iY+DLMvdqMwvb+XhqbjgyzkcN6svYVfL9MV3h6vJ -oMZ8S4ZBtUP3qN9RtE54VOPgSAHhmcbUX26cB4psSaO3CXMDNm29U3e4uZuhDZvx -F9exHVehThIqieKSjUQaSKNQzNKSstRsg18FVtNvTbOsfoUiShyhfmlzIGU+R/4/ -6m0ZMtUirg== ------END CERTIFICATE----- diff --git a/docker/traefik/docker-compose.yml b/docker/traefik/docker-compose.yml deleted file mode 100644 index 7accd54..0000000 --- a/docker/traefik/docker-compose.yml +++ /dev/null @@ -1,19 +0,0 @@ -services: - traefik: - image: traefik:2.3 # an old but reliable version - restart: unless-stopped - ports: - - "${TRAEFIK_HTTP_PORT:-80}:80" - - "${TRAEFIK_HTTPS_PORT:-443}:443" - - "${TRAEFIK_DASHBOARD_PORT:-8080}:8080" - volumes: - - ${TRAEFIK_CONFIG_FILE:-./traefik.yaml}:/etc/traefik/traefik.yaml - - ${TRAEFIK_CONFIG_DIR:-./traefik-config.d}:/etc/traefik/traefik-config.d - - ${TRAEFIK_CERTS_DIR:-./certs}:/etc/traefik/certs - - /var/run/docker.sock:/var/run/docker.sock - networks: - - traefik - -networks: - traefik: - external: true diff --git a/docker/traefik/traefik-config.d/certs.yaml b/docker/traefik/traefik-config.d/certs.yaml deleted file mode 100644 index d306419..0000000 --- a/docker/traefik/traefik-config.d/certs.yaml +++ /dev/null @@ -1,11 +0,0 @@ -tls: - stores: - default: - defaultCertificate: - certFile: /etc/traefik/certs/_wildcard.local.pem - keyFile: /etc/traefik/certs/_wildcard.local-key.pem - - certificates: - - certFile: /etc/traefik/certs/_wildcard.local.pem - keyFile: /etc/traefik/certs/_wildcard.local-key.pem - diff --git a/docker/traefik/traefik.yaml b/docker/traefik/traefik.yaml deleted file mode 100644 index 670c649..0000000 --- a/docker/traefik/traefik.yaml +++ /dev/null @@ -1,25 +0,0 @@ -api: - dashboard: true - insecure: true # expose API with no auth; harmless for local dev - -accessLog: {} - -log: - level: "INFO" - # level: "DEBUG" - -providers: - docker: - network: traefik - exposedByDefault: false - - file: - directory: "/etc/traefik/traefik-config.d" - watch: true - -entrypoints: - web: - address: ":80" - - web-secure: - address: ":443" diff --git a/docs/readme.hackathon.md b/docs/readme.hackathon.md index 1f7bbdf..d0d550b 100644 --- a/docs/readme.hackathon.md +++ b/docs/readme.hackathon.md @@ -44,7 +44,7 @@ base=https://api.aspiredev.local # base=http://localhost:8099 curl "$base/plugins/info/1.2/?action=plugin_information&slug=hello-dolly&_fair=1" -curl "$base/packages/did:web:api.aspiredev.org:packages:wp-plugin:hello-dolly" +curl "$base/packages/did:web:api.aspirecloud.localhost:packages:wp-plugin:hello-dolly" curl "$base/plugins/info/1.2/?action=query_plugins&browse=updated?_fair=1" curl "$base/packages/did:plc:afjf7gsjzsqmgc7dlhb553mv" diff --git a/justfile b/justfile new file mode 100644 index 0000000..8c4b0bb --- /dev/null +++ b/justfile @@ -0,0 +1,34 @@ + +start: wipe bootstrap + +bootstrap: prereqs build up keygen db + +prereqs: + [ -e .env ] || cp .env.example .env + mkdir -p .cache + docker network create traefik >/dev/null 2>&1 || true + docker network create fair-net >/dev/null 2>&1 || true + +build: + docker compose build + bin/dcrun composer install + bin/dcrun yarn + bin/dcrun yarn run build + +up: + docker compose up -d + +keygen: + bin/dcrun php artisan key:generate + +db: + bin/dcrun meta/bin/reset-database + bin/dcrun meta/bin/reset-testing-database + +wipe: clean + docker compose down --volumes --remove-orphans + +clean: + rm -rf vendor node_modules .cache + + diff --git a/meta/bin/_prelude.bash b/meta/bin/_prelude.bash new file mode 100644 index 0000000..488dcdf --- /dev/null +++ b/meta/bin/_prelude.bash @@ -0,0 +1,17 @@ +# This file should be sourced, not run + +set -euo pipefail + +# These are not exported, but will be visible in the tool's script if they need them +__ORIG_PWD=$PWD +__HERE=$(dirname "$0") + +function warn { echo "$@" >&2; } +function die { warn "$@"; exit 1; } + +for file in "$__HERE"/_prelude.d/*.bash; do + # shellcheck source=/dev/null + [[ -f $file ]] && source "$file" +done + +cd "$__HERE"/../.. diff --git a/meta/bin/build-images b/meta/bin/build-images index a651718..c6c90b5 100755 --- a/meta/bin/build-images +++ b/meta/bin/build-images @@ -1,12 +1,11 @@ #!/bin/bash - -. $(dirname $0)/prelude.bash +here=$(dirname "$0") && source "$here"/_prelude.bash tag=${TAG:-$(git describe --tags --abbrev=0)} platform=${PLATFORM:-linux/amd64,linux/arm64} if [[ -n $GHCR_TOKEN ]]; then - echo $GHCR_TOKEN | docker login ghcr.io -u userame-is-ignored --password-stdin + echo "$GHCR_TOKEN" | docker login ghcr.io -u userame-is-ignored --password-stdin push_arg='--push' fi @@ -14,8 +13,15 @@ buildx() { local file=$1 local name=$2 local tag=$3 - docker buildx build --platform $platform -f $file --target prod -t ghcr.io/$name:$tag -t ghcr.io/$name:latest $push_arg . + # shellcheck disable=SC2086 + docker buildx build \ + --platform "$platform" \ + -f "$file" \ + --target prod \ + -t "ghcr.io/$name:$tag" \ + -t "ghcr.io/$name:latest" \ + $push_arg . } -buildx docker/webapp/Dockerfile fairpm/aspirecloud $tag -buildx docker/laravel-worker/Dockerfile fairpm/aspirecloud-worker $tag +buildx docker/webapp/Dockerfile fairpm/aspirecloud "$tag" +buildx docker/laravel-worker/Dockerfile fairpm/aspirecloud-worker "$tag" diff --git a/meta/bin/docker-exec b/meta/bin/docker-exec deleted file mode 100755 index f8041f5..0000000 --- a/meta/bin/docker-exec +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/sh - -cd $(dirname $0)/../.. - -if which docker >/dev/null 2>&1; then - exec docker compose exec ${APP_CONTAINER:-webapp} "$@" -else - # docker not found means we're probably in the app container already - exec "$@" -fi diff --git a/meta/bin/docker-exec-composer-wrapper b/meta/bin/docker-exec-composer-wrapper deleted file mode 100755 index f507b16..0000000 --- a/meta/bin/docker-exec-composer-wrapper +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/sh - -cd $(dirname $0)/../.. - -exec meta/bin/docker-exec vendor/bin/$(basename $0) "$@" diff --git a/meta/bin/prelude.bash b/meta/bin/prelude.bash deleted file mode 100644 index b39a901..0000000 --- a/meta/bin/prelude.bash +++ /dev/null @@ -1,22 +0,0 @@ -# This file should be sourced, not run -[[ -n $TRACE ]] && [[ $TRACE != 0 ]] && set -x - -set -o errexit - -cd $(dirname $0)/../.. -base=$(pwd) - -function warn { - echo "$@" >&2 -} - -function die() { - warn "$@" - exit 1 -} - -function RUN() { - [[ -n $DRY_RUN ]] && [[ $DRY_RUN != 0 ]] && _run=echo - $_run "$@" -} - diff --git a/meta/bin/reset-database b/meta/bin/reset-database new file mode 100755 index 0000000..8576a1c --- /dev/null +++ b/meta/bin/reset-database @@ -0,0 +1,15 @@ +#!/bin/bash +here=$(dirname "$0") && source "$here"/_prelude.bash + +. .env +[[ -r .env.testing ]] && . .env.testing + +export APP_ENV=testing +export DB_CONNECTION=test + +export PGPASSWORD=$DB_ROOT_PASSWORD +psql -U "$DB_ROOT_USERNAME" -h "$DB_HOST" -c 'drop database if exists aspirecloud_testing' +psql -U "$DB_ROOT_USERNAME" -h "$DB_HOST" -c "create database aspirecloud_testing owner $DB_USERNAME" +psql -U "$DB_ROOT_USERNAME" -h "$DB_HOST" -c "create extension if not exists pg_trgm" +php artisan migrate --database=test --force --no-interaction +php artisan db:seed --database=test diff --git a/meta/bin/reset-testing-database b/meta/bin/reset-testing-database index a30dc0f..61a577b 100755 --- a/meta/bin/reset-testing-database +++ b/meta/bin/reset-testing-database @@ -1,16 +1,12 @@ #!/bin/bash - -. $(dirname $0)/prelude.bash +here=$(dirname "$0") && source "$here"/_prelude.bash . .env [[ -r .env.testing ]] && . .env.testing -export APP_ENV=testing -export DB_CONNECTION=test - export PGPASSWORD=$DB_ROOT_PASSWORD -psql -U $DB_ROOT_USERNAME -h $DB_HOST -c 'drop database if exists aspirecloud_testing' -psql -U $DB_ROOT_USERNAME -h $DB_HOST -c "create database aspirecloud_testing owner $DB_USERNAME" -psql -U $DB_ROOT_USERNAME -h $DB_HOST -c "create extension if not exists pg_trgm" -php artisan migrate --database=test --force --no-interaction -php artisan db:seed --database=test +psql -U "$DB_ROOT_USERNAME" -h "$DB_HOST" -c 'drop database if exists aspirecloud' +psql -U "$DB_ROOT_USERNAME" -h "$DB_HOST" -c "create database aspirecloud owner $DB_USERNAME" +psql -U "$DB_ROOT_USERNAME" -h "$DB_HOST" -c "create extension if not exists pg_trgm" +php artisan migrate --force --no-interaction +php artisan db:seed diff --git a/phpunit.xml b/phpunit.xml index 316df96..b07ac59 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -24,7 +24,7 @@ - + diff --git a/tests/Feature/API/WpOrg/ThemeControllerTest.php b/tests/Feature/API/WpOrg/ThemeControllerTest.php index 767f2e6..c6f62fe 100644 --- a/tests/Feature/API/WpOrg/ThemeControllerTest.php +++ b/tests/Feature/API/WpOrg/ThemeControllerTest.php @@ -66,7 +66,7 @@ ->assertStatus(200) ->assertJson([ 'author' => 'tmeister', - 'download_link' => 'https://api.aspiredev.org/download/my-theme', + 'download_link' => 'https://api.aspirecloud.localhost/download/my-theme', 'downloaded' => 1000, 'homepage' => 'https://wordpress.org/themes/my-theme/', 'last_updated' => '2025-01-18', @@ -103,7 +103,7 @@ 'user_nicename' => 'tmeister', ], 'creation_time' => '2011-11-11 11:11:11', - 'download_link' => 'https://api.aspiredev.org/download/my-theme', + 'download_link' => 'https://api.aspirecloud.localhost/download/my-theme', 'downloaded' => 1000, 'external_repository_url' => 'https://test.com', 'homepage' => 'https://wordpress.org/themes/my-theme/', @@ -186,7 +186,7 @@ ], ]) // GH-278: return all fields. these are not normally returned by default by .org - ->assertJsonPath('themes.0.download_link', 'https://api.aspiredev.org/download/my-theme') + ->assertJsonPath('themes.0.download_link', 'https://api.aspirecloud.localhost/download/my-theme') ->assertJsonPath('themes.0.downloaded', 1000) ->assertJsonPath('themes.0.active_installs', 100) ->assertJsonPath('themes.0.tags.black', 'black'); diff --git a/tests/Feature/API/WpOrg/ThemeUpdateControllerTest.php b/tests/Feature/API/WpOrg/ThemeUpdateControllerTest.php index 5a2aa50..1de32e3 100644 --- a/tests/Feature/API/WpOrg/ThemeUpdateControllerTest.php +++ b/tests/Feature/API/WpOrg/ThemeUpdateControllerTest.php @@ -120,8 +120,8 @@ 'name' => 'My Theme', 'theme' => 'my-theme', 'new_version' => '1.2.1', - 'url' => 'https://api.aspiredev.org/download/my-theme', - 'package' => 'https://api.aspiredev.org/download/my-theme', + 'url' => 'https://api.aspirecloud.localhost/download/my-theme', + 'package' => 'https://api.aspirecloud.localhost/download/my-theme', 'requires' => null, 'requires_php' => '5.6', ], @@ -131,8 +131,8 @@ 'name' => 'My Theme2', 'theme' => 'my-theme2', 'new_version' => '2.9', - 'url' => 'https://api.aspiredev.org/download/my-theme2', - 'package' => 'https://api.aspiredev.org/download/my-theme2', + 'url' => 'https://api.aspirecloud.localhost/download/my-theme2', + 'package' => 'https://api.aspirecloud.localhost/download/my-theme2', 'requires' => null, 'requires_php' => '5.6', ], @@ -185,8 +185,8 @@ 'name' => 'My Theme', 'theme' => 'my-theme', 'new_version' => '1.2.1', - 'url' => 'https://api.aspiredev.org/download/my-theme', - 'package' => 'https://api.aspiredev.org/download/my-theme', + 'url' => 'https://api.aspirecloud.localhost/download/my-theme', + 'package' => 'https://api.aspirecloud.localhost/download/my-theme', 'requires' => null, 'requires_php' => '5.6', ], @@ -238,8 +238,8 @@ 'name' => 'My Theme', 'theme' => 'my-theme', 'new_version' => '1.2.1', - 'url' => 'https://api.aspiredev.org/download/my-theme', - 'package' => 'https://api.aspiredev.org/download/my-theme', + 'url' => 'https://api.aspirecloud.localhost/download/my-theme', + 'package' => 'https://api.aspirecloud.localhost/download/my-theme', 'requires' => null, 'requires_php' => '5.6', ], @@ -293,8 +293,8 @@ 'name' => 'My Theme', 'theme' => 'my-theme', 'new_version' => '1.2.1', - 'url' => 'https://api.aspiredev.org/download/my-theme', - 'package' => 'https://api.aspiredev.org/download/my-theme', + 'url' => 'https://api.aspirecloud.localhost/download/my-theme', + 'package' => 'https://api.aspirecloud.localhost/download/my-theme', 'requires' => null, 'requires_php' => '5.6', ], @@ -302,8 +302,8 @@ 'name' => 'My Theme2', 'theme' => 'my-theme2', 'new_version' => '2.9', - 'url' => 'https://api.aspiredev.org/download/my-theme2', - 'package' => 'https://api.aspiredev.org/download/my-theme2', + 'url' => 'https://api.aspirecloud.localhost/download/my-theme2', + 'package' => 'https://api.aspirecloud.localhost/download/my-theme2', 'requires' => null, 'requires_php' => '5.6', ], @@ -339,8 +339,8 @@ "name" => "My Theme", "theme" => "my-theme", "new_version" => "1.2.1", - "url" => "https://api.aspiredev.org/download/my-theme", - "package" => "https://api.aspiredev.org/download/my-theme", + "url" => "https://api.aspirecloud.localhost/download/my-theme", + "package" => "https://api.aspirecloud.localhost/download/my-theme", "requires" => null, "requires_php" => "5.6", ], diff --git a/tests/Feature/Rewrite/RewritePluginDownloadLinkTest.php b/tests/Feature/Rewrite/RewritePluginDownloadLinkTest.php index cc33d64..a0d8fa7 100644 --- a/tests/Feature/Rewrite/RewritePluginDownloadLinkTest.php +++ b/tests/Feature/Rewrite/RewritePluginDownloadLinkTest.php @@ -62,7 +62,7 @@ ], ]; $plugin = Plugin::fromSyncMetadata($metadata); - expect($plugin->download_link)->toBe('https://api.aspiredev.org/download/plugin/0-errors.0.2.zip'); + expect($plugin->download_link)->toBe('https://api.aspirecloud.localhost/download/plugin/0-errors.0.2.zip'); }); it('returns original url if no version found', function () { diff --git a/tests/Feature/Rewrite/RewriteThemeDownloadLinkTest.php b/tests/Feature/Rewrite/RewriteThemeDownloadLinkTest.php index 3089a65..8fad94c 100644 --- a/tests/Feature/Rewrite/RewriteThemeDownloadLinkTest.php +++ b/tests/Feature/Rewrite/RewriteThemeDownloadLinkTest.php @@ -82,7 +82,7 @@ it('uses link from version if no version in download_link', function () use ($md_100b) { $theme = Theme::fromSyncMetadata($md_100b); - expect($theme->download_link)->toBe('https://api.aspiredev.org/download/theme/100-bytes.1.1.3.zip'); + expect($theme->download_link)->toBe('https://api.aspirecloud.localhost/download/theme/100-bytes.1.1.3.zip'); }); it('returns original url if no version found', function () use ($md_100b) { diff --git a/tests/Feature/Sync/SyncPluginTest.php b/tests/Feature/Sync/SyncPluginTest.php index 413d079..f9c766c 100644 --- a/tests/Feature/Sync/SyncPluginTest.php +++ b/tests/Feature/Sync/SyncPluginTest.php @@ -157,14 +157,14 @@ // test URL rewrites expect($plugin->download_link) - ->toBe('https://api.aspiredev.org/download/plugin/0-errors.0.2.zip') + ->toBe('https://api.aspirecloud.localhost/download/plugin/0-errors.0.2.zip') ->and($plugin->icons)->toBe( - ['default' => 'https://api.aspiredev.org/download/gp-icon/plugin/0-errors/head/0-errors.svg'], + ['default' => 'https://api.aspirecloud.localhost/download/gp-icon/plugin/0-errors/head/0-errors.svg'], ) ->and($plugin->versions)->toBe([ - '0.1' => 'https://api.aspiredev.org/download/plugin/0-errors.0.1.zip', - '0.2' => 'https://api.aspiredev.org/download/plugin/0-errors.0.2.zip', - 'trunk' => 'https://api.aspiredev.org/download/plugin/0-errors.zip', + '0.1' => 'https://api.aspirecloud.localhost/download/plugin/0-errors.0.1.zip', + '0.2' => 'https://api.aspirecloud.localhost/download/plugin/0-errors.0.2.zip', + 'trunk' => 'https://api.aspirecloud.localhost/download/plugin/0-errors.zip', ]); }); diff --git a/tests/Feature/Sync/SyncThemeTest.php b/tests/Feature/Sync/SyncThemeTest.php index 6c3d96e..0848c18 100644 --- a/tests/Feature/Sync/SyncThemeTest.php +++ b/tests/Feature/Sync/SyncThemeTest.php @@ -114,27 +114,27 @@ ->and($theme->external_repository_url)->toBeNull(); // test url rewrites - expect($theme->download_link)->toBe('https://api.aspiredev.org/download/theme/100-bytes.1.1.3.zip'); + expect($theme->download_link)->toBe('https://api.aspirecloud.localhost/download/theme/100-bytes.1.1.3.zip'); expect($theme->versions)->toBe([ - '1.0' => 'https://api.aspiredev.org/download/theme/100-bytes.1.0.zip', - '1.0.1' => 'https://api.aspiredev.org/download/theme/100-bytes.1.0.1.zip', - '1.0.2' => 'https://api.aspiredev.org/download/theme/100-bytes.1.0.2.zip', - '1.0.3' => 'https://api.aspiredev.org/download/theme/100-bytes.1.0.3.zip', - '1.0.4' => 'https://api.aspiredev.org/download/theme/100-bytes.1.0.4.zip', - '1.0.5' => 'https://api.aspiredev.org/download/theme/100-bytes.1.0.5.zip', - '1.0.6' => 'https://api.aspiredev.org/download/theme/100-bytes.1.0.6.zip', - '1.0.7' => 'https://api.aspiredev.org/download/theme/100-bytes.1.0.7.zip', - '1.0.8' => 'https://api.aspiredev.org/download/theme/100-bytes.1.0.8.zip', - '1.0.9' => 'https://api.aspiredev.org/download/theme/100-bytes.1.0.9.zip', - '1.1.0' => 'https://api.aspiredev.org/download/theme/100-bytes.1.1.0.zip', - '1.1.1' => 'https://api.aspiredev.org/download/theme/100-bytes.1.1.1.zip', - '1.1.2' => 'https://api.aspiredev.org/download/theme/100-bytes.1.1.2.zip', - '1.1.3' => 'https://api.aspiredev.org/download/theme/100-bytes.1.1.3.zip', + '1.0' => 'https://api.aspirecloud.localhost/download/theme/100-bytes.1.0.zip', + '1.0.1' => 'https://api.aspirecloud.localhost/download/theme/100-bytes.1.0.1.zip', + '1.0.2' => 'https://api.aspirecloud.localhost/download/theme/100-bytes.1.0.2.zip', + '1.0.3' => 'https://api.aspirecloud.localhost/download/theme/100-bytes.1.0.3.zip', + '1.0.4' => 'https://api.aspirecloud.localhost/download/theme/100-bytes.1.0.4.zip', + '1.0.5' => 'https://api.aspirecloud.localhost/download/theme/100-bytes.1.0.5.zip', + '1.0.6' => 'https://api.aspirecloud.localhost/download/theme/100-bytes.1.0.6.zip', + '1.0.7' => 'https://api.aspirecloud.localhost/download/theme/100-bytes.1.0.7.zip', + '1.0.8' => 'https://api.aspirecloud.localhost/download/theme/100-bytes.1.0.8.zip', + '1.0.9' => 'https://api.aspirecloud.localhost/download/theme/100-bytes.1.0.9.zip', + '1.1.0' => 'https://api.aspirecloud.localhost/download/theme/100-bytes.1.1.0.zip', + '1.1.1' => 'https://api.aspirecloud.localhost/download/theme/100-bytes.1.1.1.zip', + '1.1.2' => 'https://api.aspirecloud.localhost/download/theme/100-bytes.1.1.2.zip', + '1.1.3' => 'https://api.aspirecloud.localhost/download/theme/100-bytes.1.1.3.zip', ]); expect($theme->screenshot_url)->toBe( - 'https://api.aspiredev.org/download/assets/theme/100-bytes/1.1.3/screenshot.png', + 'https://api.aspirecloud.localhost/download/assets/theme/100-bytes/1.1.3/screenshot.png', ); });