name: Publish Packages on: workflow_dispatch: inputs: tag: description: "Release tag (e.g. v0.3.10)" required: true permissions: contents: write env: APP_NAME: dbx PRODUCT_NAME: DBX REPO_OWNER: t8y2 jobs: publish: name: Publish to Homebrew & Scoop runs-on: ubuntu-latest steps: - name: Extract version from tag id: version run: | TAG="${{ github.event.inputs.tag }}" TAG="$(echo "${TAG}" | xargs)" VERSION="${TAG#v}" if [[ ! "${VERSION}" =~ ^[0-9]+\.[0-9]+\.[0-9]+ ]]; then echo "::error::Tag '${TAG}' does not look like a semver version." exit 1 fi echo "version=${VERSION}" >> "$GITHUB_OUTPUT" echo "tag=${TAG}" >> "$GITHUB_OUTPUT" - name: Download release assets env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | mkdir -p artifacts VERSION="${{ steps.version.outputs.version }}" TAG="${{ steps.version.outputs.tag }}" ASSETS=( "${PRODUCT_NAME}_${VERSION}_aarch64.dmg" "${PRODUCT_NAME}_${VERSION}_x64.dmg" "${PRODUCT_NAME}_${VERSION}_x64-setup.exe" "${PRODUCT_NAME}_${VERSION}_arm64-setup.exe" ) for ASSET in "${ASSETS[@]}"; do echo "::group::Downloading ${ASSET}" gh release download "${TAG}" \ --repo "${{ github.repository }}" \ --pattern "${ASSET}" \ --dir artifacts \ --clobber echo "::endgroup::" done ls -lh artifacts/ - name: Compute SHA256 hashes id: hashes run: | VERSION="${{ steps.version.outputs.version }}" compute_hash() { local file="$1" name="$2" if [[ ! -f "${file}" ]]; then echo "::error::Missing artifact: ${file}" exit 1 fi local hash hash=$(sha256sum "${file}" | cut -d ' ' -f 1) echo "${name}=${hash}" >> "$GITHUB_OUTPUT" echo " ${name}: ${hash}" } echo "SHA256 hashes:" compute_hash "artifacts/${PRODUCT_NAME}_${VERSION}_aarch64.dmg" "sha_dmg_arm64" compute_hash "artifacts/${PRODUCT_NAME}_${VERSION}_x64.dmg" "sha_dmg_x64" compute_hash "artifacts/${PRODUCT_NAME}_${VERSION}_x64-setup.exe" "sha_exe_x64" compute_hash "artifacts/${PRODUCT_NAME}_${VERSION}_arm64-setup.exe" "sha_exe_arm64" - name: Publish Homebrew Cask env: TAP_GITHUB_TOKEN: ${{ secrets.TAP_GITHUB_TOKEN }} run: | VERSION="${{ steps.version.outputs.version }}" SHA_ARM64="${{ steps.hashes.outputs.sha_dmg_arm64 }}" SHA_X64="${{ steps.hashes.outputs.sha_dmg_x64 }}" git clone --depth 1 \ "https://x-access-token:${TAP_GITHUB_TOKEN}@github.com/${REPO_OWNER}/homebrew-tap.git" \ homebrew-tap cd homebrew-tap mkdir -p Casks cat > Casks/dbx.rb <<'RUBY_EOF' cask "dbx" do arch arm: "aarch64", intel: "x64" version "__VERSION__" sha256 arm: "__SHA_ARM64__", intel: "__SHA_X64__" url "https://github.com/__OWNER__/__APP__/releases/download/v#{version}/__PRODUCT___#{version}_#{arch}.dmg", verified: "github.com/__OWNER__/__APP__/" name "DBX" desc "Database management tool" homepage "https://dbxio.com/" depends_on macos: :big_sur app "DBX.app" zap trash: [ "~/Library/Application Support/com.dbx.app", "~/Library/Caches/com.dbx.app", "~/Library/Logs/com.dbx.app", "~/Library/Preferences/com.dbx.app.plist", ] end RUBY_EOF sed -i 's/^ //' Casks/dbx.rb sed -i "s/__VERSION__/${VERSION}/g" Casks/dbx.rb sed -i "s/__OWNER__/${REPO_OWNER}/g" Casks/dbx.rb sed -i "s/__APP__/${APP_NAME}/g" Casks/dbx.rb sed -i "s/__PRODUCT__/${PRODUCT_NAME}/g" Casks/dbx.rb sed -i "s/__SHA_ARM64__/${SHA_ARM64}/g" Casks/dbx.rb sed -i "s/__SHA_X64__/${SHA_X64}/g" Casks/dbx.rb git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" git add Casks/dbx.rb if git diff --cached --quiet; then echo "Homebrew Cask is already up to date." else git commit -m "dbx ${VERSION}" git push echo "::notice::Homebrew Cask updated to ${VERSION}" fi - name: Publish Scoop manifest env: TAP_GITHUB_TOKEN: ${{ secrets.TAP_GITHUB_TOKEN }} run: | VERSION="${{ steps.version.outputs.version }}" SHA_EXE_X64="${{ steps.hashes.outputs.sha_exe_x64 }}" SHA_EXE_ARM64="${{ steps.hashes.outputs.sha_exe_arm64 }}" git clone --depth 1 \ "https://x-access-token:${TAP_GITHUB_TOKEN}@github.com/${REPO_OWNER}/scoop-bucket.git" \ scoop-bucket cd scoop-bucket cat > dbx.json <> "$GITHUB_OUTPUT" - name: Download release assets env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | mkdir -p assets gh release download "${{ steps.version.outputs.tag }}" \ --repo "${{ github.repository }}" \ --dir assets \ --clobber ls -lh assets/ - name: Rewrite latest.json download URLs to R2 CDN run: | TAG="${{ steps.version.outputs.tag }}" LATEST_JSON="assets/latest.json" if [ ! -f "$LATEST_JSON" ]; then echo "::warning::latest.json not found, skipping URL rewrite" exit 0 fi echo "Before:" jq '.platforms[]?.url // empty' "$LATEST_JSON" jq --arg tag "$TAG" ' (.platforms[]?.url?) |= ( sub("https://github\\.com/t8y2/dbx/releases/download/" + $tag + "/"; "https://dl.dbxio.com/releases/latest/") + "?v=" + $tag ) ' "$LATEST_JSON" > "$LATEST_JSON.tmp" mv "$LATEST_JSON.tmp" "$LATEST_JSON" echo "After:" jq '.platforms[]?.url // empty' "$LATEST_JSON" - name: Upload to R2 (versioned) run: | aws s3 sync ./assets "s3://${{ secrets.R2_BUCKET_NAME }}/releases/${{ steps.version.outputs.tag }}/" \ --endpoint-url "https://${{ secrets.R2_ACCOUNT_ID }}.r2.cloudflarestorage.com" env: AWS_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_ACCESS_KEY }} - name: Upload to R2 (latest) run: | aws s3 sync ./assets "s3://${{ secrets.R2_BUCKET_NAME }}/releases/latest/" \ --endpoint-url "https://${{ secrets.R2_ACCOUNT_ID }}.r2.cloudflarestorage.com" \ --cache-control "no-cache" \ --delete env: AWS_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_ACCESS_KEY }} - name: Purge Cloudflare cache run: | set -euo pipefail zone_id="${CLOUDFLARE_ZONE_ID:-}" if [ -z "$zone_id" ]; then zone_id="$(curl -fsSL \ -H "Authorization: Bearer ${CLOUDFLARE_API_TOKEN}" \ -H "Content-Type: application/json" \ "https://api.cloudflare.com/client/v4/zones?name=dbxio.com" \ | jq -r '.result[0].id // empty')" fi if [ -z "$zone_id" ]; then echo "::error::Cloudflare zone dbxio.com not found. Set CLOUDFLARE_ZONE_ID or grant Zone:Read to CLOUDFLARE_API_TOKEN." exit 1 fi curl -fsSL -X POST \ -H "Authorization: Bearer ${CLOUDFLARE_API_TOKEN}" \ -H "Content-Type: application/json" \ --data '{"purge_everything":true}' \ "https://api.cloudflare.com/client/v4/zones/${zone_id}/purge_cache" \ | jq -e '.success == true' env: CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} CLOUDFLARE_ZONE_ID: ${{ secrets.CLOUDFLARE_ZONE_ID }} promote-release: name: Promote to Release needs: [publish, upload-r2] runs-on: ubuntu-latest steps: - name: Promote prerelease to release env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | TAG="${{ github.event.inputs.tag }}" gh release edit "${TAG}" \ --repo "${{ github.repository }}" \ --prerelease=false \ --latest echo "::notice::Release ${TAG} promoted from prerelease to latest release" notify-qq: name: QQ Notify needs: [promote-release] runs-on: ubuntu-latest steps: - name: Build message id: msg env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} TAG: ${{ github.event.inputs.tag }} run: | RELEASE_JSON=$(gh release view "${TAG}" --repo "${{ github.repository }}" --json tagName,name,url,body) export RELEASE_TAG=$(echo "$RELEASE_JSON" | jq -r '.tagName') export RELEASE_URL=$(echo "$RELEASE_JSON" | jq -r '.url') export RELEASE_BODY=$(echo "$RELEASE_JSON" | jq -r '.body') python3 - <<'PYEOF' import os, re tag = os.environ["RELEASE_TAG"] url = os.environ["RELEASE_URL"] body = os.environ.get("RELEASE_BODY", "")[:800] body = re.sub(r'#{1,6}\s*', '', body) body = re.sub(r'\*\*(.+?)\*\*', r'\1', body) body = re.sub(r'\*(.+?)\*', r'\1', body) body = re.sub(r'`(.+?)`', r'\1', body) body = re.sub(r'\[(.+?)\]\(.+?\)', r'\1', body) body = re.sub(r'\n{3,}', '\n\n', body) body = body.strip() msg = "\n".join([ "━━━━━━━━━━━━━━━", f"🚀 新版本发布 {tag}", "━━━━━━━━━━━━━━━", "", body, "", f"下载: {url}", "━━━━━━━━━━━━━━━", ]) with open(os.environ["GITHUB_OUTPUT"], "a") as f: delim = "EOF_MSG" f.write(f"msg<<{delim}\n{msg}\n{delim}\n") PYEOF - name: Send to QQ group env: SSH_KEY: ${{ secrets.DEPLOY_SSH_KEY }} HOST: ${{ secrets.DEPLOY_HOST }} ASTRBOT_API_KEY: ${{ secrets.ASTRBOT_API_KEY }} MSG_CONTENT: ${{ steps.msg.outputs.msg }} run: | mkdir -p ~/.ssh echo "$SSH_KEY" > ~/.ssh/id_ed25519 chmod 600 ~/.ssh/id_ed25519 ssh-keyscan -H "$HOST" >> ~/.ssh/known_hosts 2>/dev/null PAYLOAD=$(jq -nc --arg msg "$MSG_CONTENT" \ '{"umo": "default:GroupMessage:1087880322", "message": $msg}') printf '%s' "$PAYLOAD" | ssh "root@${HOST}" "curl -fsS -X POST http://localhost:6185/api/v1/im/message \ -H 'Authorization: Bearer ${ASTRBOT_API_KEY}' \ -H 'Content-Type: application/json' \ --data-binary @-"