name: Release on: push: tags: - "v*" workflow_dispatch: inputs: ref: description: "Git ref to build (branch/tag/SHA)" required: false default: "master" tag: description: "Release tag to publish assets to (for example: v4.14.6)" required: false permissions: contents: write jobs: build-dashboard: name: Build Dashboard runs-on: ubuntu-24.04 env: R2_ACCOUNT_ID: ${{ secrets.R2_ACCOUNT_ID }} R2_ACCESS_KEY_ID: ${{ secrets.R2_ACCESS_KEY_ID }} R2_SECRET_ACCESS_KEY: ${{ secrets.R2_SECRET_ACCESS_KEY }} steps: - name: Checkout repository uses: actions/checkout@v6 with: fetch-depth: 0 ref: ${{ inputs.ref || github.ref }} - name: Resolve tag id: tag shell: bash run: | if [ "${{ github.event_name }}" = "push" ]; then tag="${GITHUB_REF_NAME}" elif [ -n "${{ inputs.tag }}" ]; then tag="${{ inputs.tag }}" else tag="$(git describe --tags --abbrev=0)" fi if [ -z "$tag" ]; then echo "Failed to resolve tag." >&2 exit 1 fi echo "tag=$tag" >> "$GITHUB_OUTPUT" - name: Setup pnpm uses: pnpm/action-setup@v4.4.0 with: version: 10.28.2 - name: Setup Node.js uses: actions/setup-node@v6 with: node-version: '24.13.0' cache: "pnpm" cache-dependency-path: dashboard/pnpm-lock.yaml - name: Build dashboard dist shell: bash run: | pnpm --dir dashboard install --frozen-lockfile pnpm --dir dashboard run build echo "${{ steps.tag.outputs.tag }}" > dashboard/dist/assets/version cd dashboard zip -r "AstrBot-${{ steps.tag.outputs.tag }}-dashboard.zip" dist - name: Upload dashboard artifact uses: actions/upload-artifact@v7 with: name: Dashboard-${{ steps.tag.outputs.tag }} if-no-files-found: error path: dashboard/AstrBot-${{ steps.tag.outputs.tag }}-dashboard.zip - name: Upload dashboard package to Cloudflare R2 if: ${{ env.R2_ACCOUNT_ID != '' && env.R2_ACCESS_KEY_ID != '' && env.R2_SECRET_ACCESS_KEY != '' }} env: R2_BUCKET_NAME: "astrbot" R2_OBJECT_NAME: "astrbot-webui-latest.zip" VERSION_TAG: ${{ steps.tag.outputs.tag }} shell: bash run: | curl https://rclone.org/install.sh | sudo bash mkdir -p ~/.config/rclone cat < ~/.config/rclone/rclone.conf [r2] type = s3 provider = Cloudflare access_key_id = $R2_ACCESS_KEY_ID secret_access_key = $R2_SECRET_ACCESS_KEY endpoint = https://${R2_ACCOUNT_ID}.r2.cloudflarestorage.com EOF cp "dashboard/AstrBot-${VERSION_TAG}-dashboard.zip" "dashboard/${R2_OBJECT_NAME}" rclone copy "dashboard/${R2_OBJECT_NAME}" "r2:${R2_BUCKET_NAME}" --progress cp "dashboard/AstrBot-${VERSION_TAG}-dashboard.zip" "dashboard/astrbot-webui-${VERSION_TAG}.zip" rclone copy "dashboard/astrbot-webui-${VERSION_TAG}.zip" "r2:${R2_BUCKET_NAME}" --progress publish-release: name: Publish GitHub Release runs-on: ubuntu-24.04 needs: - build-dashboard steps: - name: Checkout repository uses: actions/checkout@v6 with: fetch-depth: 0 ref: ${{ inputs.ref || github.ref }} - name: Resolve tag id: tag shell: bash run: | if [ "${{ github.event_name }}" = "push" ]; then tag="${GITHUB_REF_NAME}" elif [ -n "${{ inputs.tag }}" ]; then tag="${{ inputs.tag }}" else tag="$(git describe --tags --abbrev=0)" fi if [ -z "$tag" ]; then echo "Failed to resolve tag." >&2 exit 1 fi echo "tag=$tag" >> "$GITHUB_OUTPUT" - name: Download dashboard artifact uses: actions/download-artifact@v8 with: name: Dashboard-${{ steps.tag.outputs.tag }} path: release-assets - name: Resolve release notes id: notes shell: bash run: | note_file="changelogs/${{ steps.tag.outputs.tag }}.md" if [ ! -f "$note_file" ]; then note_file="$(mktemp)" echo "Release ${{ steps.tag.outputs.tag }}" > "$note_file" fi echo "file=$note_file" >> "$GITHUB_OUTPUT" - name: Ensure release exists env: GH_TOKEN: ${{ github.token }} shell: bash run: | tag="${{ steps.tag.outputs.tag }}" if ! gh release view "$tag" >/dev/null 2>&1; then gh release create "$tag" --title "$tag" --notes-file "${{ steps.notes.outputs.file }}" fi - name: Remove stale assets from release env: GH_TOKEN: ${{ github.token }} shell: bash run: | tag="${{ steps.tag.outputs.tag }}" while IFS= read -r asset; do case "$asset" in *.AppImage|*.dmg|*.zip|*.exe|*.blockmap) gh release delete-asset "$tag" "$asset" -y || true ;; esac done < <(gh release view "$tag" --json assets --jq '.assets[].name') - name: Upload assets to release env: GH_TOKEN: ${{ github.token }} shell: bash run: | tag="${{ steps.tag.outputs.tag }}" gh release upload "$tag" release-assets/* --clobber publish-pypi: name: Publish PyPI runs-on: ubuntu-24.04 needs: - publish-release steps: - name: Checkout repository uses: actions/checkout@v6 with: fetch-depth: 0 ref: ${{ inputs.ref || github.ref }} - name: Resolve tag id: tag shell: bash run: | if [ "${{ github.event_name }}" = "push" ]; then tag="${GITHUB_REF_NAME}" elif [ -n "${{ inputs.tag }}" ]; then tag="${{ inputs.tag }}" else tag="$(git describe --tags --abbrev=0)" fi if [ -z "$tag" ]; then echo "Failed to resolve tag." >&2 exit 1 fi echo "tag=$tag" >> "$GITHUB_OUTPUT" - name: Download dashboard artifact uses: actions/download-artifact@v8 with: name: Dashboard-${{ steps.tag.outputs.tag }} path: dashboard-artifact - name: Unpack dashboard dist into package tree shell: bash run: | mkdir -p astrbot/dashboard/dist unzip -q "dashboard-artifact/AstrBot-${{ steps.tag.outputs.tag }}-dashboard.zip" -d dashboard-artifact/unpacked cp -r dashboard-artifact/unpacked/dist/. astrbot/dashboard/dist/ - name: Set up Python uses: actions/setup-python@v6 with: python-version: "3.10" - name: Install uv shell: bash run: python -m pip install uv - name: Build package shell: bash # Dashboard assets are already in astrbot/dashboard/dist/; # ASTRBOT_BUILD_DASHBOARD is intentionally unset so the hatch hook skips npm. run: uv build - name: Publish to PyPI env: UV_PUBLISH_TOKEN: ${{ secrets.PYPI_TOKEN }} shell: bash run: uv publish