name: Release on: push: tags: - 'v*' workflow_dispatch: inputs: tag: description: 'Tag to release (e.g., v1.0.0)' required: true type: string simple_release: description: 'Simple release: only x86_64 GHCR image, skip other artifacts' required: false type: boolean default: false # 环境变量:合并 workflow_dispatch 输入和 repository variable # tag push 触发时读取 vars.SIMPLE_RELEASE,workflow_dispatch 时使用输入参数 env: SIMPLE_RELEASE: ${{ github.event.inputs.simple_release == 'true' || vars.SIMPLE_RELEASE == 'true' }} permissions: contents: write packages: write jobs: # Update VERSION file with tag version update-version: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Update VERSION file run: | if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then VERSION=${{ github.event.inputs.tag }} VERSION=${VERSION#v} else VERSION=${GITHUB_REF#refs/tags/v} fi echo "$VERSION" > backend/cmd/server/VERSION echo "Updated VERSION file to: $VERSION" - name: Upload VERSION artifact uses: actions/upload-artifact@v4 with: name: version-file path: backend/cmd/server/VERSION retention-days: 1 build-frontend: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: '20' cache: 'npm' cache-dependency-path: frontend/package-lock.json - name: Install dependencies run: npm ci working-directory: frontend - name: Build frontend run: npm run build working-directory: frontend - name: Upload frontend artifact uses: actions/upload-artifact@v4 with: name: frontend-dist path: backend/internal/web/dist/ retention-days: 1 release: needs: [update-version, build-frontend] runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 with: fetch-depth: 0 ref: ${{ github.event.inputs.tag || github.ref }} - name: Download VERSION artifact uses: actions/download-artifact@v4 with: name: version-file path: backend/cmd/server/ - name: Download frontend artifact uses: actions/download-artifact@v4 with: name: frontend-dist path: backend/internal/web/dist/ - name: Setup Go uses: actions/setup-go@v5 with: go-version: '1.24' cache-dependency-path: backend/go.sum # Docker setup for GoReleaser - name: Set up QEMU uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Login to DockerHub if: ${{ env.DOCKERHUB_USERNAME != '' }} uses: docker/login-action@v3 env: DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }} with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Login to GitHub Container Registry uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }} - name: Fetch tags with annotations run: | # 确保获取完整的 annotated tag 信息 git fetch --tags --force - name: Get tag message id: tag_message run: | if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then TAG_NAME=${{ github.event.inputs.tag }} else TAG_NAME=${GITHUB_REF#refs/tags/} fi echo "Processing tag: $TAG_NAME" # 获取完整的 tag message(跳过第一行标题) TAG_MESSAGE=$(git tag -l --format='%(contents:body)' "$TAG_NAME") # 调试输出 echo "Tag message length: ${#TAG_MESSAGE}" echo "Tag message preview:" echo "$TAG_MESSAGE" | head -10 # 使用 EOF 分隔符处理多行内容 echo "message<> $GITHUB_OUTPUT echo "$TAG_MESSAGE" >> $GITHUB_OUTPUT echo "EOF" >> $GITHUB_OUTPUT - name: Set lowercase owner for GHCR id: lowercase run: echo "owner=$(echo '${{ github.repository_owner }}' | tr '[:upper:]' '[:lower:]')" >> $GITHUB_OUTPUT - name: Run GoReleaser uses: goreleaser/goreleaser-action@v6 with: version: '~> v2' args: release --clean --skip=validate ${{ env.SIMPLE_RELEASE == 'true' && '--config=.goreleaser.simple.yaml' || '' }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} TAG_MESSAGE: ${{ steps.tag_message.outputs.message }} GITHUB_REPO_OWNER: ${{ github.repository_owner }} GITHUB_REPO_OWNER_LOWER: ${{ steps.lowercase.outputs.owner }} GITHUB_REPO_NAME: ${{ github.event.repository.name }} DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME || 'skip' }} # Update DockerHub description - name: Update DockerHub description if: ${{ env.SIMPLE_RELEASE != 'true' && env.DOCKERHUB_USERNAME != '' }} uses: peter-evans/dockerhub-description@v4 env: DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }} with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} repository: ${{ secrets.DOCKERHUB_USERNAME }}/sub2api short-description: "Sub2API - AI API Gateway Platform" readme-filepath: ./deploy/DOCKER.md # Send Telegram notification - name: Send Telegram Notification if: ${{ env.SIMPLE_RELEASE != 'true' }} env: TELEGRAM_BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }} TELEGRAM_CHAT_ID: ${{ secrets.TELEGRAM_CHAT_ID }} DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }} continue-on-error: true run: | # 检查必要的环境变量 if [ -z "$TELEGRAM_BOT_TOKEN" ] || [ -z "$TELEGRAM_CHAT_ID" ]; then echo "Telegram credentials not configured, skipping notification" exit 0 fi if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then TAG_NAME=${{ github.event.inputs.tag }} else TAG_NAME=${GITHUB_REF#refs/tags/} fi VERSION=${TAG_NAME#v} REPO="${{ github.repository }}" GHCR_IMAGE="ghcr.io/${REPO,,}" # ${,,} converts to lowercase # 获取 tag message 内容 TAG_MESSAGE='${{ steps.tag_message.outputs.message }}' # 限制消息长度(Telegram 消息限制 4096 字符,预留空间给头尾固定内容) if [ ${#TAG_MESSAGE} -gt 3500 ]; then TAG_MESSAGE="${TAG_MESSAGE:0:3500}..." fi # 构建消息内容 MESSAGE="🚀 *Sub2API 新版本发布!*"$'\n'$'\n' MESSAGE+="📦 版本号: \`${VERSION}\`"$'\n'$'\n' # 添加更新内容 if [ -n "$TAG_MESSAGE" ]; then MESSAGE+="${TAG_MESSAGE}"$'\n'$'\n' fi MESSAGE+="🐳 *Docker 部署:*"$'\n' MESSAGE+="\`\`\`bash"$'\n' # 根据是否配置 DockerHub 动态生成 if [ -n "$DOCKERHUB_USERNAME" ]; then DOCKER_IMAGE="${DOCKERHUB_USERNAME}/sub2api" MESSAGE+="# Docker Hub"$'\n' MESSAGE+="docker pull ${DOCKER_IMAGE}:${TAG_NAME}"$'\n' MESSAGE+="# GitHub Container Registry"$'\n' fi MESSAGE+="docker pull ${GHCR_IMAGE}:${TAG_NAME}"$'\n' MESSAGE+="\`\`\`"$'\n'$'\n' MESSAGE+="🔗 *相关链接:*"$'\n' MESSAGE+="• [GitHub Release](https://github.com/${REPO}/releases/tag/${TAG_NAME})"$'\n' if [ -n "$DOCKERHUB_USERNAME" ]; then MESSAGE+="• [Docker Hub](https://hub.docker.com/r/${DOCKER_IMAGE})"$'\n' fi MESSAGE+="• [GitHub Packages](https://github.com/${REPO}/pkgs/container/sub2api)"$'\n'$'\n' MESSAGE+="#Sub2API #Release #${TAG_NAME//./_}" # 发送消息 curl -s -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \ -H "Content-Type: application/json" \ -d "$(jq -n \ --arg chat_id "${TELEGRAM_CHAT_ID}" \ --arg text "${MESSAGE}" \ '{ chat_id: $chat_id, text: $text, parse_mode: "Markdown", disable_web_page_preview: true }')"