threejs-test/.forgejo/workflows/ci.yaml
Juan Sebastian Montoya 26bb17c901
All checks were successful
Build and Publish Docker Image / Build and Validate (pull_request) Successful in 8s
Build and Publish Docker Image / Publish to Registry (pull_request) Has been skipped
Bugfix: Add Portainer deployment trigger to CI workflow
- Introduced a new step in the CI workflow to trigger a Portainer deployment using a webhook.
- Added checks for the presence of the PORTAINER_WEBHOOK_URL secret and handled HTTP response codes from the webhook call.
- Ensured that the workflow does not fail if the webhook call is unsuccessful.

This enhancement streamlines the deployment process by automating updates to the Portainer stack upon successful CI runs.
2025-11-26 13:21:04 -05:00

372 lines
15 KiB
YAML
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

name: Build and Publish Docker Image
on:
pull_request:
branches:
- main
push:
branches:
- main
jobs:
build-and-validate:
name: Build and Validate
runs-on: ubuntu
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Validate PR Title
if: github.event_name == 'pull_request'
run: |
PR_TITLE="${{ github.event.pull_request.title }}"
echo "PR Title: $PR_TITLE"
# Check if PR title starts with valid prefix
if [[ "$PR_TITLE" =~ ^(Release|Feature|Hotfix|Bugfix)/ ]]; then
echo "✅ PR title is valid: $PR_TITLE"
else
echo "❌ PR title must start with one of: Release/, Feature/, Hotfix/, or Bugfix/"
echo "Current title: $PR_TITLE"
exit 1
fi
- name: Build Docker Image
run: |
docker build --build-arg VERSION=test --build-arg BUILD_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ") -t threejs-test:test .
- name: Validate Image
run: |
docker run --rm -d --name test-container -p 8080:80 threejs-test:test
sleep 2
curl -f http://localhost:8080 || exit 1
docker stop test-container
- name: Job Summary
if: success()
run: |
SUMMARY_FILE="${FORGEJO_STEP_SUMMARY}"
cat >> "$SUMMARY_FILE" << 'EOF'
## ✅ Build and Validation Complete
- ✅ Docker image built successfully
- ✅ Image validated (container started and HTTP check passed)
The image is ready for deployment.
EOF
publish:
name: Publish to Registry
runs-on: ubuntu
needs: build-and-validate
if: github.event_name == 'push'
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0 # Fetch all history for tags
token: ${{ secrets.FORGEBOT_ACCESS_TOKEN }}
- name: Login to Registry
run: |
echo "${{ secrets.REGISTRY_PASSWORD }}" | docker login git.jusemon.com -u "${{ secrets.REGISTRY_USERNAME }}" --password-stdin
- name: Determine Version
id: version
run: |
# Get latest version tag
LATEST_TAG=$(git describe --tags --match 'v*.*.*' --abbrev=0 2>/dev/null || echo "v0.0.0")
LATEST_VERSION="${LATEST_TAG#v}"
# Parse version components
IFS='.' read -r MAJOR MINOR PATCH <<< "$LATEST_VERSION"
MAJOR=${MAJOR:-0}
MINOR=${MINOR:-0}
PATCH=${PATCH:-0}
# Get PR title from event or merge commit message
if [[ "${{ github.event_name }}" == "pull_request" ]]; then
PR_TITLE="${{ github.event.pull_request.title }}"
echo "PR Title: $PR_TITLE"
else
# For push events, check merge commit message
# Merge commits often have format: "Merge pull request #X from branch" or "PR Title (#X)"
COMMIT_MSG=$(git log -1 --pretty=format:"%s" HEAD)
echo "Commit message: $COMMIT_MSG"
# Try to extract PR title from merge commit
# Look for patterns like "Release/...", "Feature/...", etc. in the commit message
if echo "$COMMIT_MSG" | grep -qE "^(Release|Feature|Hotfix|Bugfix)/"; then
# If commit message itself starts with prefix, use it
PR_TITLE=$(echo "$COMMIT_MSG" | grep -oE "^(Release|Feature|Hotfix|Bugfix)/[^[:space:]]*" | head -1)
elif echo "$COMMIT_MSG" | grep -qE "(Release|Feature|Hotfix|Bugfix)/"; then
# Extract prefix pattern from anywhere in the message
PR_TITLE=$(echo "$COMMIT_MSG" | grep -oE "(Release|Feature|Hotfix|Bugfix)/[^[:space:]]*" | head -1)
else
# Check if it's a merge commit and try to get the original PR title
# For Forgejo, merge commits might reference the PR
PR_TITLE="$COMMIT_MSG"
echo "⚠️ Could not extract PR title prefix from commit message, using full message"
fi
echo "Extracted PR title: $PR_TITLE"
fi
# Determine version type from PR title prefix
if [[ "$PR_TITLE" =~ ^Release/ ]]; then
VERSION_TYPE="release"
echo "Detected: RELEASE - incrementing major version"
elif [[ "$PR_TITLE" =~ ^Feature/ ]]; then
VERSION_TYPE="feature"
echo "Detected: FEATURE - incrementing minor version"
elif [[ "$PR_TITLE" =~ ^(Hotfix|Bugfix)/ ]]; then
VERSION_TYPE="patch"
echo "Detected: HOTFIX/BUGFIX - incrementing patch version"
else
echo "⚠️ Warning: PR title does not match expected pattern (Release/, Feature/, Hotfix/, or Bugfix/)"
echo "Defaulting to FEATURE (minor version increment)"
VERSION_TYPE="feature"
fi
# Increment version based on type
if [[ "$LATEST_TAG" == "v0.0.0" ]]; then
# First version
NEW_VERSION="0.1.0"
elif [[ "$VERSION_TYPE" == "release" ]]; then
# Release: increment major version and reset minor/patch (0.1.5 -> 1.0.0)
MAJOR=$((MAJOR + 1))
MINOR=0
PATCH=0
NEW_VERSION="$MAJOR.$MINOR.$PATCH"
elif [[ "$VERSION_TYPE" == "feature" ]]; then
# Feature: increment minor version and reset patch (0.1.5 -> 0.2.0)
MINOR=$((MINOR + 1))
PATCH=0
NEW_VERSION="$MAJOR.$MINOR.$PATCH"
else
# Hotfix/Bugfix: increment patch version (0.1.5 -> 0.1.6)
PATCH=$((PATCH + 1))
NEW_VERSION="$MAJOR.$MINOR.$PATCH"
fi
echo "version=$NEW_VERSION" >> $GITHUB_OUTPUT
echo "tag=v$NEW_VERSION" >> $GITHUB_OUTPUT
echo "version_type=$VERSION_TYPE" >> $GITHUB_OUTPUT
echo "Latest tag: $LATEST_TAG"
echo "Version type: $VERSION_TYPE"
echo "New version: $NEW_VERSION"
echo "Current VERSION file: $(cat VERSION 2>/dev/null || echo 'not found')"
- name: Build Docker Image
run: |
VERSION="${{ steps.version.outputs.version }}"
BUILD_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
IMAGE_NAME="git.jusemon.com/jusemon/threejs-test:$VERSION"
docker build --build-arg VERSION="$VERSION" --build-arg BUILD_DATE="$BUILD_DATE" -t "$IMAGE_NAME" .
echo "IMAGE_NAME=$IMAGE_NAME" >> $GITHUB_ENV
- name: Push Docker Image
run: |
IMAGE_NAME="git.jusemon.com/jusemon/threejs-test:${{ steps.version.outputs.version }}"
docker push "$IMAGE_NAME"
# Also tag as 'latest' for main branch
docker tag "$IMAGE_NAME" "git.jusemon.com/jusemon/threejs-test:latest"
docker push "git.jusemon.com/jusemon/threejs-test:latest"
- name: Generate Release Notes
id: release_notes
run: |
VERSION="${{ steps.version.outputs.version }}"
TAG="${{ steps.version.outputs.tag }}"
IMAGE_NAME="git.jusemon.com/jusemon/threejs-test:$VERSION"
COMMIT_HASH=$(git rev-parse HEAD)
COMMIT_SHORT=$(git rev-parse --short HEAD)
BUILD_DATE=$(date -u +"%Y-%m-%d %H:%M:%S UTC")
# Get commits since last tag
LATEST_TAG=$(git describe --tags --match 'v*.*.*' --abbrev=0 2>/dev/null || echo "")
if [[ -n "$LATEST_TAG" ]]; then
COMMITS=$(git log ${LATEST_TAG}..HEAD --pretty=format:"- %s (%h)" --no-merges)
else
COMMITS=$(git log --pretty=format:"- %s (%h)" --no-merges -10)
fi
# Get author of the commit
COMMIT_AUTHOR=$(git log -1 --pretty=format:"%an <%ae>")
# Read template and replace placeholders
TEMPLATE_FILE=".forgejo/release-template.md"
if [[ ! -f "$TEMPLATE_FILE" ]]; then
echo "Error: Template file not found: $TEMPLATE_FILE"
exit 1
fi
# Replace placeholders in template
# Handle COMMITS separately due to multi-line content that can break sed
# First, replace all single-line placeholders
sed -e "s|{{VERSION}}|$VERSION|g" \
-e "s|{{IMAGE_NAME}}|$IMAGE_NAME|g" \
-e "s|{{COMMIT_HASH}}|$COMMIT_HASH|g" \
-e "s|{{COMMIT_SHORT}}|$COMMIT_SHORT|g" \
-e "s|{{BUILD_DATE}}|$BUILD_DATE|g" \
-e "s|{{COMMIT_AUTHOR}}|$COMMIT_AUTHOR|g" \
"$TEMPLATE_FILE" > /tmp/release_message_temp.txt
# Replace COMMITS placeholder - use a while loop to handle multi-line safely
if [[ -n "$COMMITS" ]]; then
# Write COMMITS to a temp file and use it for replacement
echo "$COMMITS" > /tmp/commits.txt
# Use a simple approach: read template line by line and replace
while IFS= read -r line; do
if [[ "$line" == *"{{COMMITS}}"* ]]; then
cat /tmp/commits.txt
else
echo "$line"
fi
done < /tmp/release_message_temp.txt > /tmp/release_message.txt
else
# If no commits, just remove the placeholder
sed 's|{{COMMITS}}||g' /tmp/release_message_temp.txt > /tmp/release_message.txt
fi
echo "Release notes generated from template"
- name: Create Git Tag
run: |
git config user.name "forgejo-actions"
git config user.email "forgejo-actions@forgejo.io"
TAG="${{ steps.version.outputs.tag }}"
# Check if tag already exists
if git rev-parse "$TAG" >/dev/null 2>&1; then
echo "Tag $TAG already exists, skipping tag creation"
echo "TAG_CREATED=false" >> $GITHUB_ENV
else
git tag -a "$TAG" -F /tmp/release_message.txt
git push origin "$TAG"
echo "Created tag $TAG with detailed release notes"
echo "TAG_CREATED=true" >> $GITHUB_ENV
fi
- name: Update Version Files
run: |
VERSION="${{ steps.version.outputs.version }}"
TAG="${{ steps.version.outputs.tag }}"
echo "📝 Updating version files to: $VERSION"
# Configure git
git config user.name "forgebot"
git config user.email "forgebot@forgejo.io"
# Fetch latest changes to avoid conflicts
git fetch origin main || echo "Fetch completed or already up to date"
git checkout main || echo "Already on main"
# Update VERSION file
echo "$VERSION" > VERSION
# Update portainer.yml with new version
sed -i "s|\(image: git.jusemon.com/jusemon/threejs-test:\)[0-9.]*|\1$VERSION|" portainer.yml
# Verify the updates
if grep -q "^$VERSION$" VERSION && grep -q "image: git.jusemon.com/jusemon/threejs-test:$VERSION" portainer.yml; then
echo "✅ Successfully updated VERSION and portainer.yml to $VERSION"
else
echo "❌ Failed to update version files"
exit 1
fi
# Check if there are changes to commit
if git diff --quiet VERSION portainer.yml; then
echo " No changes to commit (files already up to date)"
else
# Stage and commit with [skip ci] to prevent infinite loop
# Note: Forgejo Actions should respect [skip ci] in commit messages
git add VERSION portainer.yml
git commit -m "chore: update version to $VERSION [skip ci]" || {
echo "⚠️ Commit failed (may already be committed)"
exit 0
}
# Push to main branch (remote should already be configured with token)
git push origin main || {
echo "⚠️ Push failed (check token permissions)"
exit 0
}
echo "✅ Successfully committed and pushed version update to $VERSION"
fi
- name: Trigger Portainer Deployment
if: success()
run: |
VERSION="${{ steps.version.outputs.version }}"
WEBHOOK_URL="${{ secrets.PORTAINER_WEBHOOK_URL }}"
if [[ -z "$WEBHOOK_URL" ]]; then
echo "⚠️ Warning: PORTAINER_WEBHOOK_URL secret not set, skipping webhook call"
exit 0
fi
echo "🚀 Triggering Portainer deployment for version $VERSION"
# Call Portainer webhook to trigger stack update
HTTP_CODE=$(curl -s -o /tmp/webhook_response.txt -w "%{http_code}" -X POST "$WEBHOOK_URL" \
-H "Content-Type: application/json" \
--max-time 30)
if [[ "$HTTP_CODE" -ge 200 && "$HTTP_CODE" -lt 300 ]]; then
echo "✅ Successfully triggered Portainer deployment (HTTP $HTTP_CODE)"
if [[ -f /tmp/webhook_response.txt ]]; then
echo "Response: $(cat /tmp/webhook_response.txt)"
fi
else
echo "⚠️ Warning: Webhook call returned HTTP $HTTP_CODE"
if [[ -f /tmp/webhook_response.txt ]]; then
echo "Response: $(cat /tmp/webhook_response.txt)"
fi
# Don't fail the workflow if webhook fails
exit 0
fi
- name: Job Summary
if: success()
run: |
SUMMARY_FILE="${FORGEJO_STEP_SUMMARY:-/dev/stdout}"
VERSION="${{ steps.version.outputs.version }}"
TAG="${{ steps.version.outputs.tag }}"
IMAGE_NAME="git.jusemon.com/jusemon/threejs-test:$VERSION"
TAG_STATUS="${TAG_CREATED:-false}"
cat >> "$SUMMARY_FILE" << EOF
## 🚀 Release Published
**Version:** \`$VERSION\`
**Docker Image:** \`$IMAGE_NAME\`
**Git Tag:** \`$TAG\`
### Published Images
- ✅ \`$IMAGE_NAME\`
- ✅ \`git.jusemon.com/jusemon/threejs-test:latest\`
### Git Tag
EOF
if [[ "$TAG_STATUS" == "true" ]]; then
echo "- ✅ Created and pushed \`$TAG\` with release notes" >> "$SUMMARY_FILE"
else
echo "- ⚠️ Tag \`$TAG\` already exists, skipped creation" >> "$SUMMARY_FILE"
fi
cat >> "$SUMMARY_FILE" << EOF
### Version Files
- ✅ VERSION file updated to \`$VERSION\`
- ✅ portainer.yml updated to \`$VERSION\`
### Pull Command
\`\`\`bash
docker pull git.jusemon.com/jusemon/threejs-test:latest
\`\`\`
EOF