Skip to content

Commit 2a57b2d

Browse files
committed
skeleton: multi-preset local development template for devctl
0 parents  commit 2a57b2d

93 files changed

Lines changed: 19316 additions & 0 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.devctl.json‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"name": "skeleton",
3+
"adapter": "docker-compose",
4+
"compose_file": ".docker/compose.yml",
5+
"env_file": ".env",
6+
"http_port_env": "PORT_HTTP",
7+
"extra_port_envs": ["PORT_DB", "PORT_REDIS", "PORT_MAIL"],
8+
"preferred_ports": [8080, 3306, 6379, 8025],
9+
"compose_project_name": "devctl_skeleton",
10+
"env": {
11+
"PROJECT_NAME": "devctl_skeleton"
12+
},
13+
"build": false,
14+
"health_timeout_seconds": 120,
15+
"health_check_path": "/health",
16+
"health_check_statuses": [204],
17+
"extra_routes": [
18+
{"name": "mail", "port_env": "PORT_MAIL"}
19+
],
20+
"primary_service": "gateway"
21+
}

‎.docker/compose.yml‎

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
name: ${PROJECT_NAME:-project_skeleton}
2+
3+
services:
4+
gateway:
5+
build:
6+
context: ..
7+
dockerfile: .docker/nginx/Dockerfile
8+
depends_on:
9+
php:
10+
condition: service_started
11+
required: false
12+
node:
13+
condition: service_started
14+
required: false
15+
environment:
16+
APP_PRESET: ${APP_PRESET:-none}
17+
ports:
18+
- "127.0.0.1:${PORT_HTTP}:80"
19+
volumes:
20+
- ../apps:/workspace/apps:ro
21+
22+
php:
23+
profiles: ["laravel", "legacy"]
24+
build:
25+
context: ..
26+
dockerfile: .docker/php/Dockerfile
27+
depends_on:
28+
mysql:
29+
condition: service_healthy
30+
redis:
31+
condition: service_healthy
32+
working_dir: /workspace/apps
33+
environment:
34+
APP_ENV: ${APP_ENV:-local}
35+
APP_DEBUG: ${APP_DEBUG:-true}
36+
APP_KEY: ${APP_KEY:-}
37+
APP_PRESET: ${APP_PRESET:-none}
38+
DB_CONNECTION: ${DB_CONNECTION:-mysql}
39+
DB_HOST: ${DB_HOST:-mysql}
40+
DB_PORT: ${DB_PORT:-3306}
41+
DB_DATABASE: ${DB_DATABASE:-app}
42+
DB_USERNAME: ${DB_USERNAME:-app}
43+
DB_PASSWORD: ${DB_PASSWORD:-app}
44+
REDIS_HOST: ${REDIS_HOST:-redis}
45+
REDIS_PORT: ${REDIS_PORT:-6379}
46+
MAIL_HOST: ${MAIL_HOST:-mailpit}
47+
MAIL_PORT: ${MAIL_PORT:-1025}
48+
volumes:
49+
- ../apps:/workspace/apps
50+
- composer-cache:/tmp/composer-cache
51+
52+
node:
53+
profiles: ["next", "legacy"]
54+
build:
55+
context: ..
56+
dockerfile: .docker/node/Dockerfile
57+
working_dir: /workspace/apps
58+
command: ["sh", "/usr/local/bin/start-node-preset"]
59+
environment:
60+
APP_PRESET: ${APP_PRESET:-none}
61+
NODE_ENV: ${NODE_ENV:-development}
62+
VITE_API_BASE_URL: ${VITE_API_BASE_URL:-http://localhost}
63+
volumes:
64+
- ../apps:/workspace/apps
65+
- node-cache:/workspace/.cache
66+
67+
tools:
68+
build:
69+
context: ..
70+
dockerfile: .docker/tools/Dockerfile
71+
user: "${TOOLS_UID:-1000}:${TOOLS_GID:-1000}"
72+
working_dir: /workspace
73+
volumes:
74+
- ../:/workspace
75+
76+
queue:
77+
profiles: ["workers"]
78+
build:
79+
context: ..
80+
dockerfile: .docker/php/Dockerfile
81+
depends_on:
82+
mysql:
83+
condition: service_healthy
84+
redis:
85+
condition: service_healthy
86+
working_dir: /workspace/apps
87+
command: ["sh", "/usr/local/bin/start-queue-preset"]
88+
environment:
89+
APP_ENV: ${APP_ENV:-local}
90+
APP_PRESET: ${APP_PRESET:-none}
91+
DB_HOST: ${DB_HOST:-mysql}
92+
DB_PORT: ${DB_PORT:-3306}
93+
DB_DATABASE: ${DB_DATABASE:-app}
94+
DB_USERNAME: ${DB_USERNAME:-app}
95+
DB_PASSWORD: ${DB_PASSWORD:-app}
96+
REDIS_HOST: ${REDIS_HOST:-redis}
97+
REDIS_PORT: ${REDIS_PORT:-6379}
98+
volumes:
99+
- ../apps:/workspace/apps
100+
- composer-cache:/tmp/composer-cache
101+
102+
mysql:
103+
image: mysql:9.7
104+
environment:
105+
MYSQL_DATABASE: ${DB_DATABASE:-app}
106+
MYSQL_USER: ${DB_USERNAME:-app}
107+
MYSQL_PASSWORD: ${DB_PASSWORD:-app}
108+
MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:-root}
109+
ports:
110+
- "127.0.0.1:${PORT_DB}:3306"
111+
volumes:
112+
- mysql-data:/var/lib/mysql
113+
healthcheck:
114+
test: ["CMD", "mysqladmin", "ping", "-h", "127.0.0.1", "-u", "root", "-p${DB_ROOT_PASSWORD:-root}"]
115+
interval: 5s
116+
timeout: 5s
117+
retries: 20
118+
119+
redis:
120+
image: redis:8.8-alpine
121+
ports:
122+
- "127.0.0.1:${PORT_REDIS}:6379"
123+
volumes:
124+
- redis-data:/data
125+
healthcheck:
126+
test: ["CMD", "redis-cli", "ping"]
127+
interval: 5s
128+
timeout: 3s
129+
retries: 10
130+
131+
mailpit:
132+
image: axllent/mailpit:v1.30
133+
ports:
134+
- "127.0.0.1:${PORT_MAIL}:8025"
135+
136+
volumes:
137+
composer-cache:
138+
node-cache:
139+
mysql-data:
140+
redis-data:

‎.docker/nginx/Dockerfile‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
FROM nginx:1.31-alpine
2+
3+
COPY .docker/nginx/entrypoint.sh /usr/local/bin/project-gateway-entrypoint
4+
RUN chmod +x /usr/local/bin/project-gateway-entrypoint
5+
6+
ENTRYPOINT ["project-gateway-entrypoint"]
7+
CMD ["nginx", "-g", "daemon off;"]

‎.docker/nginx/entrypoint.sh‎

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
#!/bin/sh
2+
set -eu
3+
4+
preset="${APP_PRESET:-none}"
5+
6+
# >>> preset:laravel,legacy
7+
write_php_config() {
8+
cat > /etc/nginx/conf.d/default.conf <<NGINX
9+
server {
10+
listen 80;
11+
server_name _;
12+
root /workspace/apps/public;
13+
index index.php index.html;
14+
15+
client_max_body_size 64m;
16+
17+
location = /health {
18+
include fastcgi_params;
19+
fastcgi_param SCRIPT_FILENAME \$document_root/index.php;
20+
fastcgi_param DOCUMENT_ROOT \$document_root;
21+
fastcgi_pass php:9000;
22+
}
23+
24+
location / {
25+
try_files \$uri \$uri/ /index.php?\$query_string;
26+
}
27+
28+
location ~ \\.php\$ {
29+
include fastcgi_params;
30+
fastcgi_param SCRIPT_FILENAME \$document_root\$fastcgi_script_name;
31+
fastcgi_param DOCUMENT_ROOT \$document_root;
32+
fastcgi_pass php:9000;
33+
}
34+
}
35+
NGINX
36+
}
37+
# <<< preset
38+
39+
# >>> preset:next
40+
write_next_config() {
41+
cat > /etc/nginx/conf.d/default.conf <<'NGINX'
42+
server {
43+
listen 80;
44+
server_name _;
45+
46+
location = /health {
47+
proxy_pass http://node:3000/api/health;
48+
}
49+
50+
location / {
51+
proxy_http_version 1.1;
52+
proxy_set_header Host $host;
53+
proxy_set_header X-Real-IP $remote_addr;
54+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
55+
proxy_set_header X-Forwarded-Proto $scheme;
56+
proxy_set_header Upgrade $http_upgrade;
57+
proxy_set_header Connection "upgrade";
58+
proxy_pass http://node:3000;
59+
}
60+
}
61+
NGINX
62+
}
63+
# <<< preset
64+
65+
write_placeholder_config() {
66+
cat > /etc/nginx/conf.d/default.conf <<'NGINX'
67+
server {
68+
listen 80;
69+
server_name _;
70+
root /workspace/apps;
71+
index index.html;
72+
73+
location = /health {
74+
add_header Content-Type text/plain;
75+
return 204;
76+
}
77+
78+
location / {
79+
try_files $uri $uri/ =404;
80+
}
81+
}
82+
NGINX
83+
}
84+
85+
case "$preset" in
86+
# >>> preset:laravel
87+
laravel)
88+
write_php_config
89+
;;
90+
# <<< preset
91+
# >>> preset:next
92+
next)
93+
write_next_config
94+
;;
95+
# <<< preset
96+
# >>> preset:legacy
97+
legacy)
98+
write_php_config
99+
;;
100+
# <<< preset
101+
none|"")
102+
write_placeholder_config
103+
;;
104+
*)
105+
echo "Unknown APP_PRESET '${preset}'." >&2
106+
exit 1
107+
;;
108+
esac
109+
110+
exec "$@"

‎.docker/node/Dockerfile‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
FROM node:26-alpine
2+
3+
RUN apk add --no-cache git
4+
5+
COPY .docker/node/start-node-preset /usr/local/bin/start-node-preset
6+
RUN chmod +x /usr/local/bin/start-node-preset
7+
8+
WORKDIR /workspace

‎.docker/node/start-node-preset‎

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#!/bin/sh
2+
set -eu
3+
4+
preset="${APP_PRESET:-none}"
5+
6+
case "$preset" in
7+
# >>> preset:next
8+
next)
9+
cd /workspace/apps
10+
if [ ! -d node_modules ]; then
11+
npm ci
12+
fi
13+
exec npm run dev -- --hostname 0.0.0.0 --port 3000
14+
;;
15+
# <<< preset
16+
# >>> preset:legacy
17+
legacy)
18+
# No route to this container's port exists for the legacy preset --
19+
# public/index.php only ever loads the built bundle from
20+
# public/theme/dist (see .presets/legacy/vite.config.ts). Rather than
21+
# run an unreachable `vite dev` server, continuously rebuild on save
22+
# so editing frontend/ during local dev reflects in a page refresh
23+
# without a manual `make legacy-fe-build`.
24+
cd /workspace/apps/frontend
25+
if [ ! -d node_modules ]; then
26+
npm ci
27+
fi
28+
exec npm run build -- --watch
29+
;;
30+
# <<< preset
31+
*)
32+
echo "Node service is idle for preset '${preset}'."
33+
exec tail -f /dev/null
34+
;;
35+
esac

‎.docker/php/Dockerfile‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
FROM php:8.5-fpm-alpine
2+
3+
RUN apk add --no-cache \
4+
bash \
5+
curl \
6+
git \
7+
icu-dev \
8+
libzip-dev \
9+
unzip \
10+
&& docker-php-ext-install intl pdo_mysql zip
11+
12+
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
13+
COPY .docker/php/start-queue-preset /usr/local/bin/start-queue-preset
14+
15+
RUN chmod +x /usr/local/bin/start-queue-preset
16+
17+
WORKDIR /workspace

‎.docker/php/start-queue-preset‎

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#!/bin/sh
2+
set -eu
3+
4+
preset="${APP_PRESET:-none}"
5+
6+
case "$preset" in
7+
# >>> preset:laravel
8+
laravel) ;;
9+
# <<< preset
10+
# >>> preset:legacy
11+
legacy) ;;
12+
# <<< preset
13+
*)
14+
echo "No queue worker is configured for preset '${preset}'."
15+
exec tail -f /dev/null
16+
;;
17+
esac
18+
19+
cd /workspace/apps
20+
21+
if [ -f artisan ]; then
22+
exec php artisan queue:work --sleep=3 --tries=3
23+
fi
24+
25+
echo "No queue worker is configured for preset '${preset}'."
26+
exec tail -f /dev/null

‎.docker/tools/Dockerfile‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
FROM python:3.14-alpine
2+
RUN apk add --no-cache git make
3+
WORKDIR /workspace

0 commit comments

Comments
 (0)