diff --git a/.github/actions/setup-python/action.yml b/.github/actions/setup-python/action.yml
new file mode 100644
index 0000000..9c14210
--- /dev/null
+++ b/.github/actions/setup-python/action.yml
@@ -0,0 +1,40 @@
+name: Setup Python
+description: Setup Python
+
+inputs:
+  python-version:
+    description: Python version
+    required: false
+    default: "3.9"
+
+runs:
+  using: "composite"
+  steps:
+    - name: Set up Python
+      uses: actions/setup-python@v2
+      with:
+        python-version: ${{ inputs.python-version }}
+
+    - name: Install poetry
+      uses: Gr1N/setup-poetry@v7
+
+    - name: Cache Windows dependencies
+      uses: actions/cache@v2
+      if: ${{ runner.os == 'Windows' }}
+      with:
+        path: ~/AppData/Local/pypoetry/Cache/virtualenvs
+        key: ${{ runner.os }}-poetry-${{ inputs.python-version }}-${{ hashFiles('**/poetry.lock') }}
+
+    - name: Cache Linux dependencies
+      uses: actions/cache@v2
+      if: ${{ runner.os == 'Linux' }}
+      with:
+        path: ~/.cache/pypoetry/virtualenvs
+        key: ${{ runner.os }}-poetry-${{ inputs.python-version }}-${{ hashFiles('**/poetry.lock') }}
+
+    - name: Cache macOS dependencies
+      uses: actions/cache@v2
+      if: ${{ runner.os == 'macOS' }}
+      with:
+        path: ~/Library/Caches/pypoetry/virtualenvs
+        key: ${{ runner.os }}-poetry-${{ inputs.python-version }}-${{ hashFiles('**/poetry.lock') }}
\ No newline at end of file
diff --git a/.github/workflows/codecov.yml b/.github/workflows/main.yml
similarity index 98%
rename from .github/workflows/codecov.yml
rename to .github/workflows/main.yml
index 669fadb..f37f954 100644
--- a/.github/workflows/codecov.yml
+++ b/.github/workflows/main.yml
@@ -1,4 +1,4 @@
-name: Code Coverage
+name: CI
 
 on:
   push:
@@ -51,7 +51,7 @@ jobs:
           path: ./src/plugins/nonebot_bison/admin_page/dist
 
       - name: Setup Python environment
-        uses: he0119/setup-python@v0.1.0
+        uses: ./.github/actions/setup-python
         with:
           python-version: ${{ matrix.python-version }}
 
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000..d5990e1
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,38 @@
+name: Release
+
+on:
+  release:
+    types: [published]
+
+jobs:
+  build-frontend:
+    name: Build Frontend
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v3
+
+      - name: Build Frontend
+        uses: ./.github/actions/build-frontend
+
+      - name: Upload dist
+        uses: actions/upload-artifact@v3
+        with:
+          name: frontend
+          path: ./admin-frontend/build/
+  publish-pypi:
+    runs-on: ubuntu-latest
+
+    steps:
+      - uses: actions/checkout@v3
+
+      - name: Download frontend files
+        uses: actions/download-artifact@v2
+        with:
+          name: frontend
+          path: ./src/plugins/nonebot_bison/admin_page/dist
+
+      - name: Setup Python environment
+        uses: ./.github/actions/setup-python
+
+      - name: Publish PyPI
+        run: poetry publish --build -u ${{ secrets.PYPI_USERNAME }} -p ${{ secrets.PYPI_PASSWORD }} || echo "Already pushed to pypi"