- Build pushes both :latest and :0.0.1 tags so the redeploy step uses the
pinned version regardless of how the runner happened to be tagged before.
- Add a 4th step 'Redeploy stack on Portainer' that runs ONLY when the
workflow is manually dispatched with deploy=true.
- Step does idempotent DELETE+CREATE on the existing stack via Portainer REST
(PUT-edit would require more plumbing for a single-service stack).
- Smoke-tests http://<host>:3001/ after the stack recreates; logs the code
but does not hard-fail the job (network-proxy reachable via NPM upstream).
Required new secrets (in addition to existing DOCKER_USERNAME/DOCKER_PASSWORD):
- PORTAINER_URL e.g. http://192.168.1.30:9000
- PORTAINER_TOKEN API access token from a Portainer user
- PORTAINER_ENDPOINT_ID numeric endpoint ID (usually 1)
- PORTAINER_STACK_ID numeric stack ID to delete before recreating
Apply the same concurrency pattern used in the python-project-template:
- cancel-in-progress: false prevents the shared act_runner from
cancelling in-progress steps when a new push lands
- pull_request trigger ensures PRs are validated before merge
- workflow_dispatch allows manual re-runs