I do this with gitlab and Cron. Commit to main on gitlab and a ci script builds the project and sftps a zip to the staging server. A Cron job checks for new zips in the directory and unzips, then installs the update. After that it restarts the job and I have a new version ready to test in staging.