Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 105 additions & 0 deletions LARAVEL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# Laravel Support in Stedding

Stedding has been extended to support Laravel applications alongside WordPress sites. This enables you to use the same robust server provisioning and zero-downtime deployment workflow for both Laravel and WordPress projects.

## How It Works

Laravel support is implemented by:

1. Adding Laravel-specific roles (`laravel-setup` and `laravel-install`) that handle Laravel's different requirements
2. Configuring Nginx to work with Laravel's `/public` web root
3. Using Artisan commands instead of WP-CLI
4. Supporting Laravel's `.env` file format and environment variables
5. **Conditional provisioning:** Trellis roles and tasks now use the `site_type` property to ensure only the correct tasks run for each site (WordPress or Laravel). This prevents WordPress tasks from running on Laravel sites and vice versa.

## Usage

### 1. Define Laravel Sites

In your `wordpress_sites.yml` file, add a `site_type: laravel` property to sites that are Laravel projects:

```yaml
wordpress_sites:
laravel-app.com:
site_type: laravel # This identifies the site as a Laravel project
site_hosts:
- canonical: laravel-app.test
redirects:
- www.laravel-app.test
local_path: ../laravel # path targeting local Laravel project directory
admin_email: [email protected] # Required but unused for Laravel
ssl:
enabled: false
provider: self-signed
cache:
enabled: false
nginx_includes:
- roles/laravel-setup/templates/laravel-site.conf.j2 # REQUIRED for Laravel sites
env:
APP_NAME: "Laravel App"
APP_ENV: local
APP_DEBUG: true
APP_URL: http://laravel-app.test
MAIL_HOST: mailhog
MAIL_PORT: 1025
CACHE_DRIVER: file
SESSION_DRIVER: file
QUEUE_DRIVER: sync
```

### 2. Customize Nginx (Optional)

If you need additional Nginx configuration beyond the default Laravel setup, you can create custom Nginx configurations for your Laravel site at:

```
nginx-includes/laravel-app.com/custom-rules.conf.j2
```

These will be included automatically alongside the main Laravel configuration.

### 3. Provision & Deploy

Use the standard Stedding commands:

```bash
# For local development
trellis vm start

# For remote servers, provision first
ansible-playbook server.yml -e env=production

# Deploy site
ansible-playbook deploy.yml -e "site=laravel-app.com env=production"
```

## What's Different for Laravel?

- Web root is `current/public` instead of `current/web`
- Using Laravel's scheduler instead of WP Cron
- Running Artisan commands during deployment (migrations, key generation, etc.)
- Laravel-specific Nginx configuration
- Laravel-friendly `.env` file formatting
- **Conditional provisioning:** Only the correct tasks run for each site type, based on the `site_type` property.

## Advanced Options

In your site configuration, you can use these Laravel-specific options:

- `run_migrations: false` - Skip database migrations during deployment
- `cache_config: false` - Skip config caching during deployment

## Example: WordPress and Laravel in One Project

You can define both WordPress and Laravel sites in the same `wordpress_sites.yml`:

```yaml
wordpress_sites:
my-wp-site.com:
# No site_type or site_type: wordpress (default)
...
laravel-app.com:
site_type: laravel
...
```

Trellis will now only run WordPress roles for WordPress sites and Laravel roles for Laravel sites.
2 changes: 2 additions & 0 deletions deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,5 @@
when: project.repo is not defined or project.repo is not match("^ssh://.+@.+|.+@.+:.+")
roles:
- deploy
- { role: wordpress-install, tags: [wordpress, wordpress-install], when: project_type | default('wordpress') == 'wordpress' }
- { role: laravel-install, tags: [laravel, laravel-install], when: project_type | default('wordpress') == 'laravel' }
2 changes: 2 additions & 0 deletions dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,5 @@
- { role: wp-cli, tags: [wp-cli] }
- { role: wordpress-setup, tags: [wordpress, wordpress-setup] }
- { role: wordpress-install, tags: [wordpress, wordpress-install] }
- { role: laravel-setup, tags: [laravel, laravel-setup] }
- { role: laravel-install, tags: [laravel, laravel-install] }
2 changes: 2 additions & 0 deletions group_vars/development/php.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@ php_opcache_revalidate_freq: 0
xdebug_mode: 'debug'
xdebug_start_with_request: 'yes'
xdebug_discover_client_host: 1

disable_default_pool: false
12 changes: 9 additions & 3 deletions group_vars/development/vault.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,13 @@ vault_mysql_root_password: devpw
# Variables to accompany `group_vars/development/wordpress_sites.yml`
# Note: the site name (`example.com`) must match up with the site name in the above file.
vault_wordpress_sites:
example.com:
admin_password: admin
# example.com:
# admin_password: admin
# env:
# db_password: example_dbpassword
laravel-app.com: # Add this entry for the Laravel site
env:
db_password: example_dbpassword
db_password: '697521253QTrWVobjxb8fW38xtVXixnwg'
db_name: laravel-app_com_development
db_user: laravel
# Add other sensitive Laravel env vars here if needed
52 changes: 45 additions & 7 deletions group_vars/development/wordpress_sites.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,57 @@
# Define accompanying passwords/secrets in group_vars/development/vault.yml

wordpress_sites:
example.com:
# example.com:
# site_hosts:
# - canonical: example.test
# redirects:
# - www.example.test
# local_path: ../site # path targeting local Bedrock site directory (relative to Ansible root)
# admin_email: [email protected]
# multisite:
# enabled: false
# ssl:
# enabled: false
# provider: self-signed
# cache:
# enabled: false
# xmlrpc:
# enabled: false
laravel-app.com:
# Merged from laravel_sites.yml
site_type: laravel # Specify that this is a Laravel site
site_hosts:
- canonical: example.test
- canonical: laravel-app.test
redirects:
- www.example.test
local_path: ../site # path targeting local Bedrock site directory (relative to Ansible root)
admin_email: admin@example.test
- www.laravel-app.test
local_path: ../laravel # path targeting local Laravel project directory
admin_email: admin@laravel-app.test
multisite:
enabled: false
ssl:
enabled: false
provider: self-signed
cache:
enabled: false
xmlrpc:
enabled: false
nginx_includes:
- roles/laravel-setup/templates/laravel-site.conf.j2
env:
APP_NAME: "Laravel App"
APP_ENV: local
APP_DEBUG: true
APP_URL: http://laravel-app.test
MAIL_HOST: mailhog
MAIL_PORT: 1025
CACHE_DRIVER: file
SESSION_DRIVER: file
QUEUE_DRIVER: sync
DB_CONNECTION: mysql
# Use direct values instead of site_env references
db_name: laravel-app_com_development
db_user: laravel
db_password: '{{ vault_wordpress_sites["laravel-app.com"].env.db_password }}'
db_host: localhost
# Set Laravel variables explicitly
DB_DATABASE: laravel-app_com_development
DB_USERNAME: laravel
DB_HOST: localhost
6 changes: 6 additions & 0 deletions group_vars/production/vault.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,9 @@ vault_wordpress_sites:
secure_auth_salt: "generateme"
logged_in_salt: "generateme"
nonce_salt: "generateme"

laravel-app.com:
env:
db_password: laravel_dbpassword_production # Define a DB password for production
# Add other sensitive Laravel env vars here (e.g., APP_KEY, mail credentials)
# APP_KEY: "generateme" # Generate with `php artisan key:generate --show`
24 changes: 24 additions & 0 deletions group_vars/production/wordpress_sites.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,27 @@ wordpress_sites:
enabled: false
xmlrpc:
enabled: false

laravel-app.com:
# Merged from laravel_sites.yml
site_type: laravel
site_hosts:
- canonical: laravel-app.com
redirects:
- www.laravel-app.com
local_path: ../laravel
repo: [email protected]:example/laravel-app.com.git
branch: main
multisite:
enabled: false
ssl:
enabled: true
provider: letsencrypt
cache:
enabled: true
env:
APP_NAME: "Laravel App"
APP_ENV: production
APP_DEBUG: false
APP_URL: https://laravel-app.com
# End of merged section
6 changes: 6 additions & 0 deletions group_vars/staging/vault.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,9 @@ vault_wordpress_sites:
secure_auth_salt: "generateme"
logged_in_salt: "generateme"
nonce_salt: "generateme"

laravel-app.com:
env:
db_password: laravel_dbpassword_staging # Define a DB password for staging
# Add other sensitive Laravel env vars here (e.g., APP_KEY, mail credentials)
# APP_KEY: "generateme" # Generate with `php artisan key:generate --show`
22 changes: 22 additions & 0 deletions group_vars/staging/wordpress_sites.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,25 @@ wordpress_sites:
enabled: false
xmlrpc:
enabled: false

laravel-app.com:
# Merged from laravel_sites.yml
site_type: laravel
site_hosts:
- canonical: staging.laravel-app.com
local_path: ../laravel
repo: [email protected]:example/laravel-app.com.git
branch: staging
multisite:
enabled: false
ssl:
enabled: true
provider: letsencrypt
cache:
enabled: true
env:
APP_NAME: "Laravel App (Staging)"
APP_ENV: staging
APP_DEBUG: false
APP_URL: https://staging.laravel-app.com
# End of merged section
8 changes: 8 additions & 0 deletions roles/deploy/tasks/initialize.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@
path: "{{ project_root }}"
state: present

- name: Determine project type
set_fact:
project_type: "{{ project.type | default('wordpress') }}"

- name: Show detected project type
debug:
msg: "Deploying {{ project_type }} project: {{ site }}"

- name: Check if deploy_initialize_after scripts exist
stat:
path: "{{ item }}"
Expand Down
17 changes: 16 additions & 1 deletion roles/deploy/vars/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,19 @@ wordpress_env_defaults:
wp_debug_log: "{{ project_root }}/logs/debug.log"
wp_post_revisions: true

site_env: "{{ wordpress_env_defaults | combine(vault_wordpress_env_defaults | default({}), project.env | default({}), vault_wordpress_sites[site].env) }}"
laravel_env_defaults:
db_host: localhost
db_name: "{{ site | underscore }}_{{ env }}"
db_user: "{{ site | underscore }}"
APP_ENV: "{{ env }}"
APP_URL: "{{ project.ssl.enabled | default(false) | ternary('https', 'http') }}://{{ project.site_hosts | map(attribute='canonical') | first }}"
git_sha: "{{ git_clone.after }}"
release_version: "{{ deploy_helper.new_release }}"

# Determine if site is Laravel or WordPress
project_type: "{{ project.type | default('wordpress') }}"

# Choose appropriate environment defaults based on project type
env_defaults: "{{ project_type == 'laravel' | ternary(laravel_env_defaults, wordpress_env_defaults) }}"

site_env: "{{ env_defaults | combine(vault_wordpress_env_defaults | default({}), project.env | default({}), vault_wordpress_sites[site].env) }}"
56 changes: 56 additions & 0 deletions roles/laravel-install/tasks/directories.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
---
- name: Create web root of Laravel sites
file:
path: "{{ www_root }}/{{ item.key }}/{{ item.value.current_path | default('current') }}/public"
owner: "{{ web_user }}"
group: "{{ web_group }}"
mode: '0755'
state: directory
loop: "{{ wordpress_sites | dict2items | selectattr('value.site_type', 'defined') | selectattr('value.site_type', 'equalto', 'laravel') | list }}"
loop_control:
label: "{{ item.key }}"

- name: Create shared folder of Laravel sites
file:
path: "{{ www_root }}/{{ item.key }}/shared"
owner: "{{ web_user }}"
group: "{{ web_group }}"
mode: '0755'
state: directory
loop: "{{ wordpress_sites | dict2items | selectattr('value.site_type', 'defined') | selectattr('value.site_type', 'equalto', 'laravel') | list }}"
loop_control:
label: "{{ item.key }}"

- name: Create storage and bootstrap/cache directories with write permissions
vars:
laravel_dirs:
- storage
- storage/app
- storage/app/public
- storage/framework
- storage/framework/cache
- storage/framework/sessions
- storage/framework/views
- storage/logs
- bootstrap/cache
file:
path: "{{ www_root }}/{{ item[0].key }}/{{ item[0].value.current_path | default('current') }}/{{ item[1] }}"
owner: "{{ web_user }}"
group: "{{ web_group }}"
mode: '0775'
state: directory
loop: "{{ (wordpress_sites | dict2items | selectattr('value.site_type', 'defined') | selectattr('value.site_type', 'equalto', 'laravel') | list) | product(laravel_dirs) | list }}"
loop_control:
label: "{{ item[0].key }} - {{ item[1] }}"

- name: Change site owner to user
file:
path: "{{ www_root }}/{{ item.key }}"
owner: "{{ web_user }}"
group: "{{ web_group }}"
state: directory
recurse: yes
loop: "{{ wordpress_sites | dict2items | selectattr('value.site_type', 'defined') | selectattr('value.site_type', 'equalto', 'laravel') | list }}"
loop_control:
label: "{{ item.key }}"
when: chown_site_directory | default(false)
21 changes: 21 additions & 0 deletions roles/laravel-install/tasks/dotenv.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
- name: Create Laravel .env file
template:
src: "env.j2"
dest: "/tmp/{{ item.key }}.env"
mode: '0644'
owner: "{{ web_user }}"
group: "{{ web_group }}"
loop: "{{ wordpress_sites | dict2items | selectattr('value.type', 'defined') | selectattr('value.type', 'equalto', 'laravel') | list }}"
loop_control:
label: "{{ item.key }}"

- name: Copy Laravel .env file into web root
synchronize:
src: "/tmp/{{ item.key }}.env"
dest: "{{ www_root }}/{{ item.key }}/{{ item.value.current_path | default('current') }}/.env"
checksum: true
loop: "{{ wordpress_sites | dict2items | selectattr('value.type', 'defined') | selectattr('value.type', 'equalto', 'laravel') | list }}"
loop_control:
label: "{{ item.key }}"
delegate_to: "{{ inventory_hostname }}"
Loading