name: Build and Deploy VuePress on: push: branches: [ main ] workflow_dispatch: jobs: build-and-deploy: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 with: fetch-depth: 0 - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: '20' cache: 'yarn' - name: Install dependencies run: yarn install --frozen-lockfile - name: Build VuePress site run: yarn docs:build - name: Get runner public IP id: ip run: echo "ipaddr=$(curl -s https://api.ipify.org)" >> $GITHUB_OUTPUT - name: Add IP to Aliyun Security Group env: ALIYUN_ACCESS_KEY_ID: ${{ secrets.ALIYUN_ACCESS_KEY_ID }} ALIYUN_ACCESS_KEY_SECRET: ${{ secrets.ALIYUN_ACCESS_KEY_SECRET }} ALIYUN_REGION: ${{ secrets.ALIYUN_REGION }} ALIYUN_SECURITY_GROUP_ID: ${{ secrets.ALIYUN_SECURITY_GROUP_ID }} SERVER_SSH_PORT: ${{ secrets.SERVER_PORT }} RUNNER_IP: ${{ steps.ip.outputs.ipaddr }} run: | pip install aliyun-python-sdk-core aliyun-python-sdk-ecs python3 << 'EOF' import os import sys from aliyunsdkcore.client import AcsClient from aliyunsdkecs.request.v20140526 import AuthorizeSecurityGroupRequest client = AcsClient( os.environ['ALIYUN_ACCESS_KEY_ID'], os.environ['ALIYUN_ACCESS_KEY_SECRET'], os.environ['ALIYUN_REGION'] ) request = AuthorizeSecurityGroupRequest.AuthorizeSecurityGroupRequest() request.set_SecurityGroupId(os.environ['ALIYUN_SECURITY_GROUP_ID']) request.set_IpProtocol('tcp') request.set_PortRange(f"{os.environ['SERVER_SSH_PORT']}/{os.environ['SERVER_SSH_PORT']}") request.set_SourceCidrIp(f"{os.environ['RUNNER_IP']}/32") request.set_Description('GitHub Actions temporary access') try: response = client.do_action_with_exception(request) print(f"✓ Added {os.environ['RUNNER_IP']} to security group") except Exception as e: if 'InvalidPermission.Duplicate' in str(e): print(f"✓ Rule already exists for {os.environ['RUNNER_IP']}") else: print(f"✗ Error: {e}") sys.exit(1) EOF - name: Deploy to Aliyun Server uses: appleboy/scp-action@v0.1.7 with: host: ${{ secrets.SERVER_HOST }} username: ${{ secrets.SERVER_USER }} key: ${{ secrets.SERVER_SSH_KEY }} port: ${{ secrets.SERVER_PORT }} source: "docs/.vuepress/dist/*" target: ${{ secrets.DEPLOY_PATH }} strip_components: 3 overwrite: true rm: true - name: Set Permissions uses: appleboy/ssh-action@v1.0.3 with: host: ${{ secrets.SERVER_HOST }} username: ${{ secrets.SERVER_USER }} key: ${{ secrets.SERVER_SSH_KEY }} port: ${{ secrets.SERVER_PORT }} script: | # 先设置所有者(这样后续操作才有权限) chown -R ${{ secrets.SERVER_USER }}:${{ secrets.SERVER_USER }} ${{ secrets.DEPLOY_PATH }} # 目录权限:755 - 允许进入和列出 find ${{ secrets.DEPLOY_PATH }} -type d -exec chmod 755 {} \; # 文件权限:644 - 只读 find ${{ secrets.DEPLOY_PATH }} -type f -exec chmod 644 {} \; - name: Remove IP from Aliyun Security Group if: always() env: ALIYUN_ACCESS_KEY_ID: ${{ secrets.ALIYUN_ACCESS_KEY_ID }} ALIYUN_ACCESS_KEY_SECRET: ${{ secrets.ALIYUN_ACCESS_KEY_SECRET }} ALIYUN_REGION: ${{ secrets.ALIYUN_REGION }} ALIYUN_SECURITY_GROUP_ID: ${{ secrets.ALIYUN_SECURITY_GROUP_ID }} SERVER_SSH_PORT: ${{ secrets.SERVER_PORT }} RUNNER_IP: ${{ steps.ip.outputs.ipaddr }} run: | pip install aliyun-python-sdk-core aliyun-python-sdk-ecs python3 << 'EOF' import os from aliyunsdkcore.client import AcsClient from aliyunsdkecs.request.v20140526 import RevokeSecurityGroupRequest client = AcsClient( os.environ['ALIYUN_ACCESS_KEY_ID'], os.environ['ALIYUN_ACCESS_KEY_SECRET'], os.environ['ALIYUN_REGION'] ) request = RevokeSecurityGroupRequest.RevokeSecurityGroupRequest() request.set_SecurityGroupId(os.environ['ALIYUN_SECURITY_GROUP_ID']) request.set_IpProtocol('tcp') request.set_PortRange(f"{os.environ['SERVER_SSH_PORT']}/{os.environ['SERVER_SSH_PORT']}") request.set_SourceCidrIp(f"{os.environ['RUNNER_IP']}/32") try: response = client.do_action_with_exception(request) print(f"✓ Removed {os.environ['RUNNER_IP']} from security group") except Exception as e: print(f"⚠ Warning: Failed to remove rule: {e}") EOF - name: Notify Success to WeChat if: success() env: COMMIT_MSG: ${{ github.event.head_commit.message }} run: | curl "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=${{ secrets.WECHAT_WEBHOOK_KEY }}" \ -H 'Content-Type: application/json' \ -d "{ \"msgtype\": \"markdown\", \"markdown\": { \"content\": \"## ✅ 部署成功\n> 项目:**Handbook (VuePress)**\n> 分支:\\\`${{ github.ref_name }}\\\`\n> 提交者:${{ github.actor }}\n> 提交信息:${COMMIT_MSG}\n> 状态:已成功部署到生产环境\" } }" - name: Get Job Logs if: failure() id: logs run: | ERROR_LOGS=$(cat $GITHUB_STEP_SUMMARY 2>/dev/null || echo "无法获取错误日志") echo "logs<> $GITHUB_OUTPUT echo "$ERROR_LOGS" >> $GITHUB_OUTPUT echo "EOF" >> $GITHUB_OUTPUT - name: Notify Failure to WeChat if: failure() env: WORKFLOW_NAME: ${{ github.workflow }} RUN_ID: ${{ github.run_id }} JOB_NAME: ${{ github.job }} run: | FAILED_STEP=$(echo "${{ toJSON(job) }}" | jq -r '.steps[] | select(.conclusion=="failure") | .name' | head -1 || echo "未知步骤") curl "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=${{ secrets.WECHAT_WEBHOOK_KEY }}" \ -H 'Content-Type: application/json' \ -d "{ \"msgtype\": \"markdown\", \"markdown\": { \"content\": \"## ❌ 部署失败\n> 项目:**Handbook (VuePress)**\n> 分支:\\\`${{ github.ref_name }}\\\`\n> 提交者:${{ github.actor }}\n> 提交信息:${{ github.event.head_commit.message }}\n> 失败步骤:${FAILED_STEP}\n> Run ID:${RUN_ID}\n\n查看完整日志请前往 GitHub Actions\" } }"