Releasing and deploying
A MelaCMS site is built in GitHub Actions and copied to the server over SSH. The server needs only PHP 8.4 and SSH: no Composer, no Node and no build step. That is why a classic shared host is enough.
The workflow files
The installer copies four files into the site. From then on they belong to the site and can be changed freely. mela:update asks before taking a new version of one (see Updating the core).
| File | Runs on | Does |
|---|---|---|
.github/workflows/ci.yml | Push to development, pull requests | Pint check, agent instructions check, asset build, php artisan test |
.github/workflows/release.yml | Push to master | CI, then the release PR, then the deploy when a release is created |
.github/workflows/deploy.yml | Called by the release, or by hand | Builds the site and copies it to the server over SSH |
.github/dependabot.yml | Daily | A pull request for a new MelaCMS version |
The flow
- Work on
developmentwith conventional commits:feat: ...,fix: ...,feat!: ...for a breaking change. - Merge
developmentintomasterand push. - release-please opens a release PR with the next version and the
CHANGELOG.mdentry. - Merge the release PR. This creates the tag and the GitHub Release, and deploys that tag.
So a release takes two merges. Merging development into master only opens the release PR and deploys nothing.
The commit type decides the version. feat: raises the minor version and fix: raises the patch. docs:, chore: and the other types make no release, so nothing is deployed. Never edit CHANGELOG.md by hand.
One-time setup on GitHub
- Create the
developmentandmasterbranches. - Under Settings → Actions → General → Workflow permissions, enable Allow GitHub Actions to create and approve pull requests.
- Commit
package-lock.json. The workflows install withnpm ci, which needs it. - Set the variables and secrets listed at the top of
.github/workflows/deploy.yml:DEPLOY_PATH,SSH_HOST,SSH_USER,SSH_KEYandMAINTENANCE_TOKEN. - When
phpon the server's command line is older than 8.4, which is common on shared hosting, also set thePHP_BINvariable to the 8.4 binary, for examplephp8.4.
One-time setup on the server
- PHP 8.4 on the command line, with the extensions listed under Requirements. Check with
php -vandphp -mover SSH: the command-line PHP can differ from the one serving the site. - The web root points to
public/insideDEPLOY_PATH. - A
.envinDEPLOY_PATHwithAPP_KEY,APP_URLand the database. The deploy never copies or changes it.
What a deploy does
- Builds the site in GitHub Actions: Composer without the development packages, and the Vite assets.
- Copies the result to
DEPLOY_PATHover SSH. - On the server: checks the PHP version, removes the cached config, routes and events of the previous release, puts the site in maintenance mode, runs the migrations, makes the
public/storagelink, builds the caches and brings the site back up.
The script stops at the first error, so a failed migration leaves the site in maintenance mode.
The first super-admin on the server
The deploy runs the migrations and creates no account, so after the first deploy nobody can log in yet. Run this once, over SSH, in the app directory on the server:
php artisan mela:super-adminIt creates the three roles if they are missing, then asks for an email, a name and a password. If the email belongs to an existing user, that user keeps their name and password and gains the role. So the same command also restores access to a site that has lost its last super-admin. The other users are then created in the panel.
Deploying again
- After a failed deploy, fix the cause, then use Re-run failed jobs on the workflow run, or
gh run rerun <run id> --failed. The variables and secrets are read again. - To deploy any tag or branch, use Actions → Deploy → Run workflow.
Troubleshooting
| Problem | Cause | Fix |
|---|---|---|
| The deploy stops with "'php' on the server is PHP 8.3..." | The command-line php is older than 8.4 | Set the PHP_BIN variable, then run the failed deploy again |
The copy step ends with dial tcp ...:22: i/o timeout | The server did not answer in time. Nothing was copied | Run the failed deploy again |
development was merged into master and nothing was deployed | That merge only opens the release PR | Merge the release PR |
| Nobody can log in on a freshly deployed site | The deploy creates no account | php artisan mela:super-admin over SSH |
Dependabot's update fails with dependency_file_not_resolvable | No PHP version pin in composer.json | The pin, committed |
Known limits
- The deploy copies over the live directory. It is not atomic, and files removed from the repository stay on the server.
- There is no rollback step.
- A host without SSH is not supported.
A site that is not deployed through GitHub Actions can delete the deploy job and deploy.yml. Versions and the changelog keep working without them.