Here's a solution that should give you clean rollbacks while keeping per-deployment assets in distinct directories on your CDN, and let Envoyer deployments use the correct ASSET_URL automatically:
1. Build assets in CI
As you're already doing, run Vite's build process in your GitHub Actions workflow. This produces your manifest and hashed files.
2. Upload assets to CDN using a unique build path
Your approach of {cdn}/builds/{github.sha}/assets is perfect because it keeps each build isolated.
3. Deploy built assets and manifest
Sync the public/build directory and the resulting manifest.json to your CDN at the correct build path during CI.
4. Pass ASSET_URL into the deployment process
Option 1: Use .env file swapping in Envoyer
Envoyer allows you to run custom deployment hooks, including ones that can update the .env file before (or after) the symlink is swapped.
How to do it:
- In your GitHub Actions workflow, after build, output the build SHA to a temporary file.
- Upload it with your assets, or output it as an Envoyer environment variable using Envoyer's "Update Environment File" deployment hook.
Example Bash script for Envoyer deployment hook:
php artisan env:set ASSET_URL=https://cdn.example.com/builds/{{ sha }}/assets
Where {{ sha }} could be set as an Envoyer "Deployment Variable", or you echo it as part of the build and pass to the deployment via API or custom scripting. See: Envoyer deployment variables docs.
Option 2: Parameterize ASSET_URL at runtime
For rollbacks, it's crucial the correct value is always available for that release. One clean approach is to use an .env template file in each release with a placeholder (like ASSET_URL=$BUILD_ASSET_URL) and during your CI step, replace it with the actual value:
In GitHub Actions:
echo "ASSET_URL=https://cdn.example.com/builds/${{ github.sha }}/assets" > .env.asset_url
Upload/copy that to your release directory, maybe as part of your Envoyer deploy.
In Envoyer Deployment Hook:
cat .env.asset_url >> .env
Or, if you want to fully replace the value:
sed -i "s|^ASSET_URL=.*$|ASSET_URL=https://cdn.example.com/builds/${DEPLOY_SHA}/assets|" .env
(Where ${DEPLOY_SHA} is passed from GitHub Actions or as an Envoyer variable.)
Option 3: Use release-based asset paths in Laravel config
If you don't want to touch the .env file, you could add logic in a Laravel config file:
// config/app.php
'asset_url' => env('ASSET_URL', 'https://cdn.example.com/builds/' . trim(file_get_contents(base_path('REVISION')), "\n") . '/assets')
And then, ensure your CI/deployer writes the commit sha or build ID to a REVISION file with every release.
5. Cleanup old CDN builds
Finally, address CDN asset bloat by running a scheduled script or GitHub action to prune old {sha}-named asset folders, except for current and N fallbacks.
Summary
- Stick with
{cdn}/builds/{sha}/assetsfor assets, uploaded via CI. - Update each release's
.env(or config) with the right per-release ASSET_URL, via CI or an Envoyer hook. - When rolling back, Envoyer checks out the previous release, whose
.env(or REVISION file) points to the correct asset path. - Cleanup CDN as needed.
This matches how tools like Laravel Vapor and other modern platforms handle rollbacks with immutable assets.
Example workflow overview:
name: Deploy
on:
push:
branches: [main]
jobs:
build_upload_deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: npm ci && npm run build
- run: aws s3 sync public/build s3://your-bucket/builds/${GITHUB_SHA}/assets
- run: echo "ASSET_URL=https://cdn.example.com/builds/${GITHUB_SHA}/assets" > .env.asset_url
# Add a step to call the Envoyer API or set an Envoyer variable with ${GITHUB_SHA}
# Deploy using Envoyer, which uses .env.asset_url or a template hook to set .env
Let me know if you need example scripts or a more automated approach for Envoyer!