Files
deep-research-web-ui/.gitea/workflows/publish-registry.yml
Jonathan Rampersad eb8fbbfd94
Some checks failed
Docker Build, Push, and Deploy to Portainer / Build and Push Docker Image (push) Successful in 1m35s
Docker Build, Push, and Deploy to Portainer / Deploy Application to Portainer (push) Failing after 3s
Change registry url for pulling image
2025-05-30 14:18:38 -04:00

151 lines
6.8 KiB
YAML

name: Docker Build, Push, and Deploy to Portainer
on:
push:
branches:
- main
env:
REGISTRY_TOKEN: ${{ secrets.GLOBAL_PACKAGE_TOKEN }}
REGISTRY_USER: ${{ vars.GLOBAL_ACCOUNT_EMAIL }}
jobs:
build-and-push:
runs-on: ubuntu-latest
name: Build and Push Docker Image
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Login to Gitea Package Regsitry
uses: docker/login-action@v3
with:
registry: ${{ vars.REGISTRY_URL }}
username: ${{ env.REGISTRY_USER }}
password: ${{ env.REGISTRY_TOKEN }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build and Push Docker Image
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile
push: true
tags: |
${{ vars.REGISTRY_URL }}/${{ gitea.repository }}:latest
${{ vars.REGISTRY_URL }}/${{ gitea.repository }}:${{ gitea.sha }}
- name: Logout from Gitea Package Regsitry
if: always()
run: docker logout ${{vars.REGISTRY_URL}}
deploy-to-portainer:
runs-on: ubuntu-latest
name: Deploy Application to Portainer
needs: build-and-push
env:
PORTAINER_URL: ${{ secrets.PORTAINER_URL }}
PORTAINER_API_KEY: ${{ secrets.PORTAINER_API_KEY }}
PORTAINER_ENVIRONMENT_ID: ${{ secrets.PORTAINER_ENVIRONMENT_ID }}
IMAGE_TO_DEPLOY: ${{ vars.REGISTRY_URL }}${{ gitea.repository }}:latest
CONTAINER_NAME: ${{ vars.PORTAINER_CONTAINER_NAME || 'deep-research' }} # Name of the container in Portainer
HOST_PORT: "8005" # Host port to map to container's 3000
CONTAINER_PORT: "3000" # Container port
RESTART_POLICY: "unless-stopped"
# For curl: -k allows insecure connections (e.g. self-signed certs). Omit for production with valid certs.
# CURL_OPTS: "-s -k" # If you need -k
CURL_OPTS: "-s" # Silent mode
steps:
- name: Deploy Container to Portainer via API
run: |
set -e # Exit immediately if a command exits with a non-zero status.
echo "Starting deployment of image ${{ env.IMAGE_TO_DEPLOY }} as container ${{ env.CONTAINER_NAME }}"
PORTAINER_API_DOCKER_BASE="${{ env.PORTAINER_URL }}/api/endpoints/${{ env.PORTAINER_ENVIRONMENT_ID }}/docker"
AUTH_HEADER="X-API-Key: ${{ env.PORTAINER_API_KEY }}"
# 1. Find existing container by name
echo "Searching for existing container named '${{ env.CONTAINER_NAME }}'..."
# Portainer API often prepends '/' to container names in the .Names field.
# Using jq to filter based on any name in the .Names array matching /<CONTAINER_NAME>
EXISTING_CONTAINER_ID=$(curl ${{ env.CURL_OPTS }} -H "$AUTH_HEADER" \
"${PORTAINER_API_DOCKER_BASE}/containers/json?all=true" | \
jq -r --arg CN "/${{ env.CONTAINER_NAME }}" '.[] | select(.Names[] | contains($CN)) | .Id' | head -n 1)
# 2. If container exists, stop and remove it
if [ -n "$EXISTING_CONTAINER_ID" ]; then
echo "Found existing container '${{ env.CONTAINER_NAME }}' with ID '$EXISTING_CONTAINER_ID'."
echo "Stopping container..."
curl ${{ env.CURL_OPTS }} -X POST -H "$AUTH_HEADER" "${PORTAINER_API_DOCKER_BASE}/containers/${EXISTING_CONTAINER_ID}/stop"
echo "Waiting for container to stop..."
sleep 5 # Adjust sleep time as needed
echo "Removing container..."
curl ${{ env.CURL_OPTS }} -X DELETE -H "$AUTH_HEADER" "${PORTAINER_API_DOCKER_BASE}/containers/${EXISTING_CONTAINER_ID}"
echo "Container '${{ env.CONTAINER_NAME }}' removed."
else
echo "No existing container named '${{ env.CONTAINER_NAME }}' found."
fi
# 3. Create new container
echo "Creating new container '${{ env.CONTAINER_NAME }}' with image '${{ env.IMAGE_TO_DEPLOY }}'..."
CREATE_PAYLOAD=$(cat <<EOF
{
"Image": "${{ env.IMAGE_TO_DEPLOY }}",
"ExposedPorts": {
"${{ env.CONTAINER_PORT }}/tcp": {}
},
"HostConfig": {
"PortBindings": {
"${{ env.CONTAINER_PORT }}/tcp": [ { "HostIp": "0.0.0.0", "HostPort": "${{ env.HOST_PORT }}" } ]
},
"RestartPolicy": { "Name": "${{ env.RESTART_POLICY }}", "MaximumRetryCount": 0 }
}
}
EOF
)
CREATE_RESPONSE=$(curl ${{ env.CURL_OPTS }} -X POST -H "$AUTH_HEADER" -H "Content-Type: application/json" \
--data "$CREATE_PAYLOAD" \
"${PORTAINER_API_DOCKER_BASE}/containers/create?name=${{ env.CONTAINER_NAME }}")
NEW_CONTAINER_ID=$(echo "$CREATE_RESPONSE" | jq -r '.Id')
WARNINGS=$(echo "$CREATE_RESPONSE" | jq -r '.Warnings // empty') # Handle null warnings
if [ -z "$NEW_CONTAINER_ID" ] || [ "$NEW_CONTAINER_ID" == "null" ]; then
echo "Failed to create container '${{ env.CONTAINER_NAME }}'."
echo "Response from Portainer API: $CREATE_RESPONSE"
exit 1
fi
echo "Container '${{ env.CONTAINER_NAME }}' created successfully with ID '$NEW_CONTAINER_ID'."
if [ -n "$WARNINGS" ]; then
echo "Warnings from Portainer during creation: $WARNINGS"
fi
# 4. Start the new container
echo "Starting container '${{ env.CONTAINER_NAME }}' (ID: $NEW_CONTAINER_ID)..."
# We capture HTTP code and response body separately for better error diagnosis.
HTTP_CODE_START=$(curl ${{ env.CURL_OPTS }} -w "%{http_code}" -o /tmp/portainer_start_response.txt -X POST \
-H "$AUTH_HEADER" \
"${PORTAINER_API_DOCKER_BASE}/containers/${NEW_CONTAINER_ID}/start")
START_RESPONSE_BODY=$(cat /tmp/portainer_start_response.txt)
if [ "$HTTP_CODE_START" -eq 204 ]; then
echo "Container '${{ env.CONTAINER_NAME }}' started successfully."
elif [ "$HTTP_CODE_START" -eq 304 ]; then # Not Modified - already started
echo "Container '${{ env.CONTAINER_NAME }}' was already started."
else
echo "Failed to start container '${{ env.CONTAINER_NAME }}'. Portainer responded with HTTP code: $HTTP_CODE_START"
echo "Response body: $START_RESPONSE_BODY"
# Fetch logs for the container to help diagnose start issues
sleep 2
echo "Fetching last 20 lines of logs for container '$NEW_CONTAINER_ID':"
curl ${{ env.CURL_OPTS }} -H "$AUTH_HEADER" \
"${PORTAINER_API_DOCKER_BASE}/containers/${NEW_CONTAINER_ID}/logs?stdout=true&stderr=true&tail=20&timestamps=true"
exit 1
fi
echo "Deployment of '${{ env.CONTAINER_NAME }}' completed."