Skip to content
This repository was archived by the owner on Apr 21, 2026. It is now read-only.

Commit a677f90

Browse files
bpamiriclaude
andcommitted
feat: Deploy via Portainer API and reduce task history limit
Switch from `docker stack deploy` CLI to Portainer REST API so the stack becomes a first-class Portainer-managed resource. Set swarm task-history-limit to 2 to prevent shutdown replica accumulation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent df16cf6 commit a677f90

1 file changed

Lines changed: 59 additions & 6 deletions

File tree

.github/workflows/swarm-deploy.yml

Lines changed: 59 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -125,13 +125,66 @@ jobs:
125125
run: |
126126
echo "$GHCR_TOKEN" | docker login ghcr.io -u "$GH_ACTOR" --password-stdin
127127
128-
- name: Deploy stack to swarm
128+
- name: Set task history limit
129+
run: docker swarm update --task-history-limit 2
130+
131+
- name: Deploy stack via Portainer API
132+
env:
133+
PORTAINER_URL: ${{ secrets.PORTAINER_URL }}
134+
PORTAINER_API_KEY: ${{ secrets.PORTAINER_API_KEY }}
135+
PORTAINER_ENDPOINT_ID: ${{ secrets.PORTAINER_ENDPOINT_ID }}
136+
run: |
137+
COMPOSE_CONTENT=$(cat deploy/swarm/docker-compose.yml)
138+
139+
# Get the swarm ID from Portainer
140+
SWARM_ID=$(curl -sf \
141+
-H "X-API-Key: ${PORTAINER_API_KEY}" \
142+
"${PORTAINER_URL}/api/endpoints/${PORTAINER_ENDPOINT_ID}/docker/swarm" \
143+
| jq -r '.ID')
144+
echo "Swarm ID: ${SWARM_ID}"
145+
146+
# Check if the stack already exists
147+
STACK_INFO=$(curl -sf \
148+
-H "X-API-Key: ${PORTAINER_API_KEY}" \
149+
"${PORTAINER_URL}/api/stacks" \
150+
| jq -r '.[] | select(.Name == "wheels-dev")')
151+
152+
if [ -n "$STACK_INFO" ]; then
153+
STACK_ID=$(echo "$STACK_INFO" | jq -r '.Id')
154+
echo "Updating existing stack (ID: ${STACK_ID})..."
155+
HTTP_CODE=$(curl -s -o /tmp/portainer-response.json -w "%{http_code}" \
156+
-X PUT \
157+
-H "X-API-Key: ${PORTAINER_API_KEY}" \
158+
-H "Content-Type: application/json" \
159+
"${PORTAINER_URL}/api/stacks/${STACK_ID}?endpointId=${PORTAINER_ENDPOINT_ID}" \
160+
-d "$(jq -n --arg content "$COMPOSE_CONTENT" '{
161+
"stackFileContent": $content,
162+
"prune": true
163+
}')")
164+
else
165+
echo "Creating new stack..."
166+
HTTP_CODE=$(curl -s -o /tmp/portainer-response.json -w "%{http_code}" \
167+
-X POST \
168+
-H "X-API-Key: ${PORTAINER_API_KEY}" \
169+
-H "Content-Type: application/json" \
170+
"${PORTAINER_URL}/api/stacks/create/swarm/string?endpointId=${PORTAINER_ENDPOINT_ID}" \
171+
-d "$(jq -n --arg name "wheels-dev" --arg swarm "$SWARM_ID" --arg content "$COMPOSE_CONTENT" '{
172+
"name": $name,
173+
"swarmID": $swarm,
174+
"stackFileContent": $content
175+
}')")
176+
fi
177+
178+
echo "Portainer API response code: ${HTTP_CODE}"
179+
if [ "$HTTP_CODE" -lt 200 ] || [ "$HTTP_CODE" -ge 300 ]; then
180+
echo "Error response:"
181+
cat /tmp/portainer-response.json
182+
exit 1
183+
fi
184+
echo "Stack deployed successfully via Portainer"
185+
186+
- name: Force pull latest image
129187
run: |
130-
docker stack deploy \
131-
--with-registry-auth \
132-
-c deploy/swarm/docker-compose.yml \
133-
wheels-dev
134-
# Force service update to pull the new :latest image on all nodes
135188
docker service update \
136189
--with-registry-auth \
137190
--image ghcr.io/wheels-dev/wheels-dev:latest \

0 commit comments

Comments
 (0)