Co-authored-by: Kay Kayyali <kaykayyali@gmail.com> Co-committed-by: Kay Kayyali <kaykayyali@gmail.com>
118 lines
4.7 KiB
YAML
118 lines
4.7 KiB
YAML
name: 'Portainer Stack Deploy'
|
|
description: 'Deploys or updates a Docker Compose stack via the Portainer API with registry auth, env vars, and health verification. Designed to be called by gitea-deploy-orchestrator or directly.'
|
|
inputs:
|
|
portainer_url:
|
|
description: 'URL to Portainer instance (e.g., http://portainer:9000)'
|
|
required: false
|
|
default: 'http://portainer:9000'
|
|
portainer_token:
|
|
description: 'Portainer API Token. May be a long-lived token, or a short-lived token issued by Hermes (see hermes-contract.md).'
|
|
required: true
|
|
endpoint_id:
|
|
description: 'Portainer Environment/Endpoint ID'
|
|
required: false
|
|
default: '1'
|
|
stack_name:
|
|
description: 'Name of the stack to create or update. REQUIRED. No default — auto-deriving from repo name produces buggy names like owner/repo on Gitea path-style.'
|
|
required: true
|
|
compose_file:
|
|
description: 'Path to docker-compose.yml file'
|
|
required: false
|
|
default: 'docker-compose.yml'
|
|
env_file:
|
|
description: 'Optional path to a .env file. Lines are added to the stack env (existing stack env is overwritten).'
|
|
required: false
|
|
default: ''
|
|
env_vars:
|
|
description: 'Comma-separated key=value pairs (added to env_file, override on conflict).'
|
|
required: false
|
|
default: ''
|
|
image:
|
|
description: 'Specific image:tag to pin the stack to (e.g. git.homelab.local:8443/owner/repo:abc123d). If set, overrides image references in compose_file and forces pullImage: true.'
|
|
required: false
|
|
default: ''
|
|
image_digest:
|
|
description: 'For rollbacks: the specific sha256 digest to deploy. Mutually exclusive with image.'
|
|
required: false
|
|
default: ''
|
|
rollback:
|
|
description: 'If true, re-deploys the previous image tag from Portainer stack history. image/image_digest are ignored when this is true.'
|
|
required: false
|
|
default: 'false'
|
|
previous_image_count:
|
|
description: 'When rollback=true, how many previous images to keep. Default 2.'
|
|
required: false
|
|
default: '2'
|
|
force_pull:
|
|
description: 'Always pullImage:true, even on updates. Default true. Set false if you trust the local image cache.'
|
|
required: false
|
|
default: 'true'
|
|
prune:
|
|
description: 'prune:true on updates — removes stopped containers for the stack. Default true.'
|
|
required: false
|
|
default: 'true'
|
|
registry_url:
|
|
description: 'Private registry URL if images need auth (e.g., git.homelab.local:8443)'
|
|
required: false
|
|
default: ''
|
|
registry_user:
|
|
description: 'Registry username'
|
|
required: false
|
|
default: ''
|
|
registry_pass:
|
|
description: 'Registry password or token'
|
|
required: false
|
|
default: ''
|
|
healthcheck_url:
|
|
description: 'URL to poll after deploy (e.g., https://app.example.com/health). Empty = skip verification.'
|
|
required: false
|
|
default: ''
|
|
healthcheck_retries:
|
|
description: 'Max retry attempts for healthcheck'
|
|
required: false
|
|
default: '12'
|
|
healthcheck_delay:
|
|
description: 'Seconds between healthcheck retries'
|
|
required: false
|
|
default: '10'
|
|
output_file:
|
|
description: 'Path to write a JSON result file. Schema: {status, action, stack_id, stack_name, image, image_digest, duration_seconds, healthcheck_status, error}.'
|
|
required: false
|
|
default: ''
|
|
fail_on_healthcheck:
|
|
description: 'If true (default), a healthcheck failure exits non-zero. If false, the deploy is reported as success-with-warning.'
|
|
required: false
|
|
default: 'true'
|
|
|
|
runs:
|
|
using: "composite"
|
|
steps:
|
|
- name: Deploy to Portainer
|
|
shell: bash
|
|
env:
|
|
PORTAINER_URL: ${{ inputs.portainer_url }}
|
|
PORTAINER_TOKEN: ${{ inputs.portainer_token }}
|
|
ENDPOINT_ID: ${{ inputs.endpoint_id }}
|
|
STACK_NAME: ${{ inputs.stack_name }}
|
|
COMPOSE_FILE: ${{ inputs.compose_file }}
|
|
ENV_FILE: ${{ inputs.env_file }}
|
|
ENV_VARS: ${{ inputs.env_vars }}
|
|
IMAGE: ${{ inputs.image }}
|
|
IMAGE_DIGEST: ${{ inputs.image_digest }}
|
|
ROLLBACK: ${{ inputs.rollback }}
|
|
PREVIOUS_IMAGE_COUNT: ${{ inputs.previous_image_count }}
|
|
FORCE_PULL: ${{ inputs.force_pull }}
|
|
PRUNE: ${{ inputs.prune }}
|
|
REGISTRY_URL: ${{ inputs.registry_url }}
|
|
REGISTRY_USER: ${{ inputs.registry_user }}
|
|
REGISTRY_PASS: ${{ inputs.registry_pass }}
|
|
HEALTHCHECK_URL: ${{ inputs.healthcheck_url }}
|
|
HEALTHCHECK_RETRIES: ${{ inputs.healthcheck_retries }}
|
|
HEALTHCHECK_DELAY: ${{ inputs.healthcheck_delay }}
|
|
OUTPUT_FILE: ${{ inputs.output_file }}
|
|
FAIL_ON_HEALTHCHECK: ${{ inputs.fail_on_healthcheck }}
|
|
REPO_NAME: ${{ github.event.repository.name }}
|
|
GITHUB_SHA: ${{ github.sha }}
|
|
GITHUB_RUN_ID: ${{ github.run_id }}
|
|
run: ${{ github.action_path }}/deploy.sh
|