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',
);
});