A production workflow should promote a known commit, not run an unbounded shell script against live files. Separate checks from deployment and make the artifact, environment, and rollback target visible.
Trigger narrowly
Run pull-request validation separately from production. Deploy only from the protected production branch or an approved release event. Add concurrency control so two production jobs cannot race. Pin third-party actions to reviewed versions or commit SHAs and grant the workflow only required permissions.
Build once and protect secrets
Install deterministically and run the same quality checks required locally. Build a standalone archive or container image once and attach the commit identity. Store deployment credentials in a protected environment, never in workflow files or artifacts. Verify the destination host identity when using SSH.
Verify before declaring success
Upload to a new release directory, start it, and probe health plus representative dynamic, image, and asset routes. Switch traffic only after those checks. Record the active and previous release and automatically restore the prior target when post-deploy validation fails.