- Introduced a step to validate PR titles, ensuring they start with the correct prefixes (Release/, Feature/, Hotfix/, Bugfix/). - Enhanced versioning logic to determine version type based on PR title or merge commit message, allowing for appropriate version increments. - Improved handling of versioning for different types of changes, including major, minor, and patch updates. This update aims to enforce consistent PR title formatting and streamline version management in the CI process.
340 lines
13 KiB
YAML
340 lines
13 KiB
YAML
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: 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
|
||
|