| 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 |
|
|
| |
| |
| env: |
| SIMPLE_RELEASE: ${{ github.event.inputs.simple_release == 'true' || vars.SIMPLE_RELEASE == 'true' }} |
|
|
| permissions: |
| contents: write |
| packages: write |
|
|
| jobs: |
| |
| update-version: |
| runs-on: ubuntu-latest |
| steps: |
| - name: Checkout |
| uses: actions/checkout@v6 |
|
|
| - 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@v7 |
| with: |
| name: version-file |
| path: backend/cmd/server/VERSION |
| retention-days: 1 |
|
|
| build-frontend: |
| runs-on: ubuntu-latest |
| steps: |
| - name: Checkout |
| uses: actions/checkout@v6 |
|
|
| - name: Setup pnpm |
| uses: pnpm/action-setup@v4 |
| with: |
| version: 9 |
|
|
| - name: Setup Node.js |
| uses: actions/setup-node@v6 |
| with: |
| node-version: '20' |
| cache: 'pnpm' |
| cache-dependency-path: frontend/pnpm-lock.yaml |
|
|
| - name: Install dependencies |
| run: pnpm install --frozen-lockfile |
| working-directory: frontend |
|
|
| - name: Build frontend |
| run: pnpm run build |
| working-directory: frontend |
|
|
| - name: Upload frontend artifact |
| uses: actions/upload-artifact@v7 |
| 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@v6 |
| with: |
| fetch-depth: 0 |
| ref: ${{ github.event.inputs.tag || github.ref }} |
|
|
| - name: Download VERSION artifact |
| uses: actions/download-artifact@v8 |
| with: |
| name: version-file |
| path: backend/cmd/server/ |
|
|
| - name: Download frontend artifact |
| uses: actions/download-artifact@v8 |
| with: |
| name: frontend-dist |
| path: backend/internal/web/dist/ |
|
|
| - name: Setup Go |
| uses: actions/setup-go@v6 |
| with: |
| go-version-file: backend/go.mod |
| check-latest: false |
| cache-dependency-path: backend/go.sum |
|
|
| - name: Verify Go version |
| run: | |
| go version | grep -q 'go1.26.1' |
| |
| |
| - 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=$(git tag -l --format='%(contents:body)' "$TAG_NAME") |
|
|
| |
| echo "Tag message length: ${#TAG_MESSAGE}" |
| echo "Tag message preview:" |
| echo "$TAG_MESSAGE" | head -10 |
|
|
| |
| echo "message<<EOF" >> $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@v7 |
| 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' }} |
|
|
| |
| - name: Update DockerHub description |
| if: ${{ env.SIMPLE_RELEASE != 'true' && env.DOCKERHUB_USERNAME != '' }} |
| uses: peter-evans/dockerhub-description@v5 |
| 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 |
|
|
| |
| - 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,,}" |
|
|
| |
| TAG_MESSAGE='${{ steps.tag_message.outputs.message }}' |
| TAG_MESSAGE=$(echo "$TAG_MESSAGE" | sed 's/\([_*`\[]\)/\\\1/g') |
|
|
| |
| if [ ${ |
| 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' |
| |
| if [ -n "$DOCKERHUB_USERNAME" ]; then |
| DOCKER_IMAGE="${DOCKERHUB_USERNAME}/sub2api" |
| MESSAGE+="# Docker Hub"$'\n' |
| MESSAGE+="docker pull ${DOCKER_IMAGE}:${TAG_NAME}"$'\n' |
| MESSAGE+=" |
| 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 |
|
|
| |
| 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 |
| }')" |
| |
| sync-version-file: |
| needs: [release] |
| if: ${{ needs.release.result == 'success' }} |
| runs-on: ubuntu-latest |
| steps: |
| - name: Checkout default branch |
| uses: actions/checkout@v6 |
| with: |
| ref: ${{ github.event.repository.default_branch }} |
| |
| - name: Sync VERSION file to released tag |
| run: | |
| if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then |
| VERSION=${{ github.event.inputs.tag }} |
| VERSION=${VERSION#v} |
| else |
| VERSION=${GITHUB_REF#refs/tags/v} |
| fi |
| |
| CURRENT_VERSION=$(tr -d '\r\n' < backend/cmd/server/VERSION || true) |
| if [ "$CURRENT_VERSION" = "$VERSION" ]; then |
| echo "VERSION file already matches $VERSION" |
| exit 0 |
| fi |
|
|
| echo "$VERSION" > backend/cmd/server/VERSION |
|
|
| git config user.name "github-actions[bot]" |
| git config user.email "41898282+github-actions[bot]@users.noreply.github.com" |
| git add backend/cmd/server/VERSION |
| git commit -m "chore: sync VERSION to ${VERSION} [skip ci]" |
| git push origin HEAD:${{ github.event.repository.default_branch }} |
|
|