fix: preserve terminal state across navigation #48
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Docker Build & Deploy | |
| on: | |
| push: | |
| branches: | |
| - master | |
| tags: | |
| - v* | |
| env: | |
| IMAGE_NAME: nonebot-webui | |
| FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true" | |
| DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME || secrets.DOCKER_HUB_USERNAME }} | |
| DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN || secrets.DOCKERHUB_PASSWORD || secrets.DOCKER_HUB_ACCESS_TOKEN }} | |
| TEST_SERVER_HOST: ${{ secrets.TEST_SERVER_HOST || '72.31.92.61' }} | |
| TEST_SERVER_USER: ${{ secrets.TEST_SERVER_USER || 'xisoul' }} | |
| jobs: | |
| build: | |
| name: Build & Push Docker Image | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| packages: write | |
| outputs: | |
| version: ${{ steps.meta.outputs.version }} | |
| image_tag: ${{ steps.image_tag.outputs.TAG }} | |
| app_version: ${{ steps.app_version.outputs.VERSION }} | |
| app_minor_version: ${{ steps.app_minor_version.outputs.VERSION }} | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v5 | |
| - name: Setup QEMU | |
| uses: docker/setup-qemu-action@v3 | |
| - name: Setup Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Login to GitHub Container Registry | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Login to Docker Hub | |
| if: ${{ env.DOCKERHUB_USERNAME != '' && env.DOCKERHUB_TOKEN != '' }} | |
| uses: docker/login-action@v3 | |
| with: | |
| username: ${{ env.DOCKERHUB_USERNAME }} | |
| password: ${{ env.DOCKERHUB_TOKEN }} | |
| - name: Warn when Docker Hub push is disabled | |
| if: ${{ env.DOCKERHUB_USERNAME == '' || env.DOCKERHUB_TOKEN == '' }} | |
| run: | | |
| echo "Docker Hub credentials are not configured. This run will only push to GHCR." | |
| - name: Read package version | |
| id: app_version | |
| run: | | |
| VERSION="$(awk -F'\"' '/^version = \"/ {print $2; exit}' pyproject.toml)" | |
| echo "VERSION=$VERSION" >> "$GITHUB_OUTPUT" | |
| - name: Read package major.minor version | |
| id: app_minor_version | |
| run: | | |
| VERSION="$(awk -F'\"' '/^version = \"/ {print $2; exit}' pyproject.toml)" | |
| MAJOR_MINOR="$(echo "$VERSION" | awk -F'.' '{print $1 "." $2}')" | |
| echo "VERSION=$MAJOR_MINOR" >> "$GITHUB_OUTPUT" | |
| - name: Generate image tag | |
| id: image_tag | |
| run: | | |
| if [[ "$GITHUB_REF" == refs/tags/v* ]]; then | |
| TAG="${GITHUB_REF#refs/tags/v}" | |
| else | |
| TAG="${GITHUB_REF_NAME}" | |
| fi | |
| echo "TAG=$TAG" >> "$GITHUB_OUTPUT" | |
| - name: Docker metadata | |
| id: meta | |
| uses: docker/metadata-action@v5 | |
| with: | |
| images: | | |
| ghcr.io/${{ github.repository_owner }}/${{ env.IMAGE_NAME }} | |
| ${{ env.DOCKERHUB_USERNAME != '' && format('docker.io/{0}/{1}', env.DOCKERHUB_USERNAME, env.IMAGE_NAME) || '' }} | |
| tags: | | |
| type=ref,event=branch | |
| type=semver,pattern={{version}} | |
| type=semver,pattern={{major}}.{{minor}} | |
| type=raw,value=latest,enable={{is_default_branch}} | |
| type=raw,value=${{ steps.app_version.outputs.VERSION }},enable=${{ github.ref == 'refs/heads/master' }} | |
| type=raw,value=${{ steps.app_minor_version.outputs.VERSION }},enable=${{ github.ref == 'refs/heads/master' }} | |
| - name: Build and Push | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: . | |
| platforms: linux/amd64 | |
| push: true | |
| build-args: | | |
| APP_VERSION=${{ steps.app_version.outputs.VERSION }} | |
| VCS_REF=${{ github.sha }} | |
| PYTHON_IMAGE=3.11 | |
| VARIANT=slim | |
| tags: ${{ steps.meta.outputs.tags }} | |
| labels: ${{ steps.meta.outputs.labels }} | |
| cache-from: type=gha | |
| cache-to: type=gha,mode=max | |
| - name: Print published tags | |
| run: | | |
| echo "Published image tags:" | |
| echo "${{ steps.meta.outputs.tags }}" | |
| deploy: | |
| name: Deploy to Test Server | |
| runs-on: ubuntu-latest | |
| needs: build | |
| if: github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/v') | |
| steps: | |
| - name: Check deploy configuration | |
| id: deploy_config | |
| env: | |
| TEST_SERVER_PASSWORD: ${{ secrets.TEST_SERVER_PASSWORD }} | |
| run: | | |
| if [[ -n "$TEST_SERVER_PASSWORD" ]]; then | |
| echo "enabled=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "enabled=false" >> "$GITHUB_OUTPUT" | |
| echo "Test server password is not configured; skipping deployment." | |
| fi | |
| - name: Deploy via SSH | |
| if: steps.deploy_config.outputs.enabled == 'true' | |
| uses: appleboy/ssh-action@v1 | |
| with: | |
| host: ${{ env.TEST_SERVER_HOST }} | |
| username: ${{ env.TEST_SERVER_USER }} | |
| password: ${{ secrets.TEST_SERVER_PASSWORD }} | |
| script: | | |
| set -e | |
| docker_cmd() { | |
| if docker info >/dev/null 2>&1; then | |
| docker "$@" | |
| else | |
| sudo docker "$@" | |
| fi | |
| } | |
| for path in \ | |
| /home/xisoul/nonebot-webui-data \ | |
| /home/xisoul/nonebot-webui-data/projects \ | |
| /home/xisoul/nonebot-webui-external-projects | |
| do | |
| if [[ ! -d "$path" ]]; then | |
| sudo mkdir -p "$path" | |
| fi | |
| done | |
| for file in \ | |
| /home/xisoul/nonebot-webui-data/config.json \ | |
| /home/xisoul/nonebot-webui-data/project.json | |
| do | |
| if [[ ! -f "$file" ]]; then | |
| sudo touch "$file" | |
| fi | |
| sudo chmod 666 "$file" | |
| done | |
| if [[ -n "${{ env.DOCKERHUB_USERNAME }}" ]]; then | |
| IMAGE="docker.io/${{ env.DOCKERHUB_USERNAME }}/nonebot-webui:${{ needs.build.outputs.image_tag }}" | |
| else | |
| IMAGE="ghcr.io/${{ github.repository_owner }}/nonebot-webui:${{ needs.build.outputs.image_tag }}" | |
| fi | |
| docker_cmd pull "$IMAGE" | |
| docker_cmd rm -f nonebot-webui 2>/dev/null || true | |
| docker_cmd run -d \ | |
| --name nonebot-webui \ | |
| --restart=always \ | |
| --network host \ | |
| -e HOST=0.0.0.0 \ | |
| -e PORT=18080 \ | |
| -v /home/xisoul/nonebot-webui-data/projects:/projects \ | |
| -v /home/xisoul/nonebot-webui-external-projects:/external-projects \ | |
| -v /home/xisoul/nonebot-webui-data/config.json:/app/config.json \ | |
| -v /home/xisoul/nonebot-webui-data/project.json:/app/project.json \ | |
| "$IMAGE" | |
| echo "Waiting for container to be healthy..." | |
| for i in $(seq 1 30); do | |
| STATUS=$(docker_cmd inspect -f '{{if .State.Health}}{{.State.Health.Status}}{{else}}{{.State.Status}}{{end}}' nonebot-webui 2>/dev/null || echo unknown) | |
| echo " [$i/30] status: $STATUS" | |
| if [[ "$STATUS" == "healthy" ]]; then | |
| exit 0 | |
| fi | |
| sleep 2 | |
| done | |
| docker_cmd logs --tail 50 nonebot-webui | |
| exit 1 |