Skip to content

version 3.1.0

version 3.1.0 #109

name: Auto Docker Build
on:
pull_request:
branches:
- release
types: [opened, synchronize, reopened]
jobs:
check-source-branch:
name: Validate PR Source Branch
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: read
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Verify PR is from main branch
uses: actions/github-script@v6
with:
script: |
console.log("Checking PR source branch...");
const { data: pr } = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.issue.number
});
if (pr.head.ref !== 'main') {
core.setFailed(`PR must be from the main branch, but was from: ${pr.head.ref}`);
process.exit(1);
}
console.log("PR is from main branch. Proceeding with release.");
release-docker-image:
name: Release Docker Image
needs: check-source-branch
runs-on: ubuntu-latest
permissions:
contents: write
packages: write
pull-requests: write
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "22.14.0"
cache: "npm"
cache-dependency-path: "frontend/package-lock.json"
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: "1.24"
cache: true
cache-dependency-path: backend/go.sum
- name: Read version from VERSION file
id: read_version
run: |
version=$(cat VERSION)
echo "Version: $version"
echo "version=$version" >> $GITHUB_OUTPUT
- name: Check if version tag exists
id: check_tag
run: |
TAG_EXISTS=$(git ls-remote --tags origin refs/tags/v${{ steps.read_version.outputs.version }} | wc -l)
echo "tag_exists=$TAG_EXISTS" >> $GITHUB_OUTPUT
- name: Exit if tag already exists
if: steps.check_tag.outputs.tag_exists != '0'
run: |
echo "Version v${{ steps.read_version.outputs.version }} already released. Skipping."
exit 0
- name: Get PR Info via GitHub API
id: pr_data
uses: actions/github-script@v6
with:
script: |
console.log("Fetching PR data...");
const { data: pr } = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.issue.number
});
if (!pr) {
core.setFailed("No matching PR found.");
return;
}
core.setOutput("title", pr.title);
core.setOutput("body", pr.body || "No description");
const commits = await github.rest.pulls.listCommits({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.issue.number
});
// Clean commit messages to prevent shell interpretation issues
const messages = commits.data.map(c => {
// Sanitize commit message to remove or escape characters that could be interpreted as commands
const cleanMessage = c.commit.message
// .replace(/`/g, "'") // Replace backticks with single quotes
// .replace(/\(/g, "'") // Replace parentheses with single quotes
// .replace(/\)/g, "'") // Replace parentheses with single quotes
// .replace(/\$/g, "") // Remove dollar signs
// .replace(/\\/g, "") // Remove backslashes
// .replace(/;/g, ","); // Replace semicolons with commas
return `- ${cleanMessage} (${c.commit.author.name})`;
});
core.setOutput("commit_log", messages.join('\\n'));
- name: Call Gemini to generate release summary
id: ai_summary
env:
PR_TITLE: ${{ steps.pr_data.outputs.title }}
PR_BODY: ${{ steps.pr_data.outputs.body }}
COMMIT_LOG: ${{ steps.pr_data.outputs.commit_log }}
run: |
VERSION=v${{ steps.read_version.outputs.version }}
if [ -z "${{ vars.GEMINI_MODEL }}" ] || [ -z "${{ secrets.GEMINI_API_KEY }}" ]; then
echo "::error::GEMINI_MODEL or GEMINI_API_KEY secret is missing. Check repository secrets/permissions."
exit 1
fi
# Write commit logs to file to avoid shell interpretation issues
printf "%s\n" "$COMMIT_LOG" > commit_log.txt
COMMITS=$(cat commit_log.txt)
PROMPT=$(printf "Release version: %s\n\nPull Request: %s\n\nDescription:\n%s\n\nCommits:\n%s\n\nYou are an agent responsible for generating GitHub release summaries from pull request commits. Write the summary using only a numbered list under the relevant categories: New Features, Improvements, or Bug Fixes. Only include categories that are present based on the commit messages. Do not add any introductory sentences like \"This release includes...\" or \"In this version...\". Go straight to the list, and describe each item as specifically as possible based on the available commit details. Maintain a clear, professional tone appropriate for public changelogs." "$VERSION" "$PR_TITLE" "$PR_BODY" "$COMMITS")
echo "Prompt: $PROMPT"
RESPONSE=$(curl -sS "https://generativelanguage.googleapis.com/v1beta/models/${{ vars.GEMINI_MODEL }}:generateContent?key=${{ secrets.GEMINI_API_KEY }}" \
-H 'Content-Type: application/json' \
-X POST \
-d '{
"contents": [{
"parts": [{"text": '"$(jq -Rs <<< "$PROMPT")"'}]
}]
}' 2>&1) || true
SUMMARY=$(echo "$RESPONSE" | jq -r '.candidates[0].content.parts[0].text // empty' 2>/dev/null || true)
if [ -z "$SUMMARY" ] || [ "$SUMMARY" = "null" ]; then
echo "::error::Failed to generate summary from Gemini or parse the response."
echo "Raw Response:"
echo "$RESPONSE"
exit 1
fi
# Generate Docker Images section using helper script
chmod +x .github/scripts/generate-docker-section.sh
DOCKER_SECTION=$(.github/scripts/generate-docker-section.sh "${{ github.repository }}" "${{ steps.read_version.outputs.version }}")
echo "title=$VERSION" >> $GITHUB_OUTPUT
# Use a tightly bound EOF to prevent string evaluation
echo "summary<<EOF_SUMMARY" >> $GITHUB_OUTPUT
echo "$SUMMARY" >> $GITHUB_OUTPUT
echo "$DOCKER_SECTION" >> $GITHUB_OUTPUT
echo "" >> $GITHUB_OUTPUT
echo "---" >> $GITHUB_OUTPUT
echo "*This summary was automatically generated by Gemini AI*" >> $GITHUB_OUTPUT
echo "EOF_SUMMARY" >> $GITHUB_OUTPUT
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
# - name: Set up QEMU (for ARM emulation)
# uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
buildkitd-flags: --debug
driver-opts: |
image=moby/buildkit:latest
network=host
- name: Build and Push Multi-Arch Docker image
uses: docker/build-push-action@v5
with:
context: .
file: Dockerfile
push: true
platforms: linux/amd64
# platforms: linux/amd64,linux/arm64
cache-from: type=registry,ref=ghcr.io/${{ github.repository }}:buildcache
cache-to: type=registry,ref=ghcr.io/${{ github.repository }}:buildcache,mode=max
build-args: |
NODE_VERSION=22.14.0
BUILDKIT_INLINE_CACHE=1
tags: |
ghcr.io/${{ github.repository }}:${{ steps.read_version.outputs.version }}
ghcr.io/${{ github.repository }}:latest
- name: Create Git Tag
run: |
git config user.name "${{ github.actor }}"
git config user.email "${{ github.actor }}@users.noreply.github.com"
git tag v${{ steps.read_version.outputs.version }}
git push origin v${{ steps.read_version.outputs.version }}
- name: Create GitHub Release
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
with:
tag_name: v${{ steps.read_version.outputs.version }}
name: ${{ steps.ai_summary.outputs.title }}
body: ${{ steps.ai_summary.outputs.summary }}
- name: Update CHANGELOG.md
env:
SUMMARY: ${{ steps.ai_summary.outputs.summary }}
run: |
tempfile=$(mktemp)
VERSION=v${{ steps.read_version.outputs.version }}
# Write new release section
{
echo "## $VERSION"
echo "_Released on $(date +'%Y-%m-%d')_"
echo ""
printf '%s\n' "$SUMMARY"
echo ""
} >> "$tempfile"
# Append existing CHANGELOG if exists
[ -f CHANGELOG.md ] && cat CHANGELOG.md >> "$tempfile"
mv "$tempfile" CHANGELOG.md
echo "=== CHANGELOG.md Preview ==="
head -n 50 CHANGELOG.md
echo "==========================="
rm -f commit_log.txt
- name: Create Pull Request to update CHANGELOG.md
uses: peter-evans/create-pull-request@4e1beaa7521e8b457b572c090b25bd3db56bf1c5 # v5.0.3
with:
commit-message: "chore: update CHANGELOG.md for v${{ steps.read_version.outputs.version }}"
title: "Update CHANGELOG.md for v${{ steps.read_version.outputs.version }}"
body: "This PR updates the changelog after release."
branch: changelog/update-${{ steps.read_version.outputs.version }}
base: main
auto-merge-pr:
name: Auto Merge PR
needs: release-docker-image
runs-on: ubuntu-latest
if: success()
permissions:
contents: write
pull-requests: write
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Set PR number from context
id: pr_number
run: |
echo "pr_number=${{ github.event.pull_request.number }}" >> $GITHUB_OUTPUT
echo "Found PR #${{ github.event.pull_request.number }}"
- name: Auto Merge PR Release
uses: actions/github-script@v6
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const pr_number = ${{ steps.pr_number.outputs.pr_number }};
if (!pr_number) {
core.setFailed("No PR number found");
return;
}
console.log(`Attempting to merge PR #${pr_number}...`);
try {
await github.rest.pulls.merge({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: pr_number,
merge_method: 'merge',
commit_title: `[Automated] Merge PR #${pr_number} to release branch`,
commit_message: `Auto-merged after successful Docker image build`
});
console.log(`Successfully merged PR #${pr_number}`);
} catch (error) {
core.setFailed(`Failed to merge PR: ${error.message}`);
}
create-public-release:
name: Create Public GitHub Release
needs: auto-merge-pr
runs-on: ubuntu-latest
if: success()
permissions:
contents: read
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Read version from VERSION file
id: read_version
run: |
version=$(cat VERSION)
echo "Version: $version"
echo "version=$version" >> $GITHUB_OUTPUT
- name: Checkout target public repository
uses: actions/checkout@v3
with:
repository: yogasw/beo-echo-release
token: ${{ secrets.PUBLIC_REPO_PAT }}
path: public-repo
- name: Copy release notes to public repo
run: |
cp CHANGELOG.md public-repo/
cd public-repo
if [[ -z $(git status --porcelain) ]]; then
echo "No changes to commit"
else
git config user.name "${{ github.actor }}"
git config user.email "${{ github.actor }}@users.noreply.github.com"
git add CHANGELOG.md
git commit -m "Update changelog for v${{ steps.read_version.outputs.version }}"
git push
fi
- name: Get Release Info
id: release_info
run: |
VERSION=v${{ steps.read_version.outputs.version }}
# Extract release notes from first occurrence of version to next version or end of file
SUMMARY=$(awk "/^## $VERSION/,/^## [^$VERSION]/" CHANGELOG.md | sed '/^## '"$VERSION"'/d' | sed '/^## /,$d')
echo "summary<<EOF" >> $GITHUB_OUTPUT
echo "$SUMMARY" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
- name: Create Public GitHub Release
uses: softprops/action-gh-release@a06a81a03ee405af7f2048a818ed3f03bbf83c7b # v2.5.0
with:
token: ${{ secrets.PUBLIC_REPO_PAT }}
repository: yogasw/beo-echo-release
tag_name: v${{ steps.read_version.outputs.version }}
name: v${{ steps.read_version.outputs.version }}
body: ${{ steps.release_info.outputs.summary }}
draft: false
prerelease: false
trigger-deployment:
needs: create-public-release
runs-on: ubuntu-latest
if: success()
steps:
- name: Trigger Deployment
run: |
curl -X POST -d {} ${{ secrets.BUILD_HOOK }}