# App Expo 统一部署 Pipeline # # 环境映射(按触发源自动推断): # main → dev (开发 + 内部测试) # staging → stage (预发布) # v*.*.* → prod (正式发布) # # 手动触发:workflow_dispatch 可选择环境 name: App Expo Deploy on: push: branches: [main, staging] tags: ['v*.*.*'] paths: - "app-expo/**" - ".github/workflows/app-expo-deploy.yml" workflow_dispatch: inputs: environment: description: '部署环境' required: true type: choice options: - dev - stage - prod default: dev version: description: '版本号 (prod 时使用,如 1.0.0)' required: false type: string concurrency: group: app-expo-deploy-${{ github.ref }} cancel-in-progress: true env: APP_NAME: app-expo jobs: deploy: name: Build & Deploy runs-on: ubuntu-latest permissions: contents: read # GitHub Environments: 在 Repo Settings → Environments 中创建 dev/staging/production,可配置独立 secrets environment: ${{ (github.event_name == 'workflow_dispatch' && github.event.inputs.environment == 'prod') && 'production' || (github.event_name == 'workflow_dispatch' && github.event.inputs.environment == 'stage') && 'staging' || startsWith(github.ref, 'refs/tags/v') && 'production' || github.ref == 'refs/heads/staging' && 'staging' || 'dev' }} steps: - name: Checkout code uses: actions/checkout@v4 with: fetch-depth: ${{ startsWith(github.ref, 'refs/tags/') && '0' || '1' }} - name: Determine environment id: env run: | if [[ "${{ github.ref }}" == refs/tags/v* ]]; then echo "env=prod" elif [[ "${{ github.ref }}" == refs/heads/staging ]]; then echo "env=stage" elif [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then echo "env=${{ github.event.inputs.environment }}" else echo "env=dev" fi >> $GITHUB_OUTPUT - name: Set up Node.js uses: actions/setup-node@v4 with: node-version: 20 cache: npm cache-dependency-path: app-expo/package-lock.json - name: Install dependencies working-directory: app-expo run: npm ci - name: Quality checks (dev only) if: steps.env.outputs.env == 'dev' working-directory: app-expo run: | npm run format:check npm run lint npm run test:ci - name: Export web build working-directory: app-expo run: npx expo export -p web - name: Determine version (prod) if: steps.env.outputs.env == 'prod' id: version run: | if [[ "${{ github.ref }}" == refs/tags/v* ]]; then VERSION="${GITHUB_REF#refs/tags/v}" TAG_NAME="${GITHUB_REF#refs/tags/}" elif [[ "${{ github.event_name }}" == "workflow_dispatch" ]] && [[ -n "${{ github.event.inputs.version }}" ]]; then VERSION="${{ github.event.inputs.version }}" TAG_NAME="v${VERSION}" else VERSION=$(node -p "require('./app-expo/package.json').version") TAG_NAME="v${VERSION}" fi echo "version=${VERSION}" >> $GITHUB_OUTPUT echo "tag_name=${TAG_NAME}" >> $GITHUB_OUTPUT - name: Create release zip (prod) if: steps.env.outputs.env == 'prod' run: | cd app-expo/dist zip -r ../${{ env.APP_NAME }}-v${{ steps.version.outputs.version }}-web.zip . - name: Generate Release Notes (prod) if: steps.env.outputs.env == 'prod' && startsWith(github.ref, 'refs/tags/') id: release_notes run: | TAG_NAME="${{ steps.version.outputs.tag_name }}" PREV_TAG=$(git tag --sort=-creatordate | grep '^v' | sed -n '2p') if [ -n "$PREV_TAG" ]; then CHANGES=$(git log "${PREV_TAG}..HEAD" --pretty=format:"- %s (%h)" --no-merges) else CHANGES=$(git log --pretty=format:"- %s (%h)" --no-merges -20) fi { echo "notes<> $GITHUB_OUTPUT - name: Upload artifact (dev / stage) if: steps.env.outputs.env != 'prod' uses: actions/upload-artifact@v4 with: name: app-expo-web-${{ steps.env.outputs.env }} path: app-expo/dist retention-days: ${{ steps.env.outputs.env == 'dev' && '14' || '30' }} - name: Upload artifact (prod) if: steps.env.outputs.env == 'prod' uses: actions/upload-artifact@v4 with: name: ${{ env.APP_NAME }}-v${{ steps.version.outputs.version }}-web path: app-expo/${{ env.APP_NAME }}-v${{ steps.version.outputs.version }}-web.zip retention-days: 90 - name: Create GitHub Release (prod) if: steps.env.outputs.env == 'prod' uses: softprops/action-gh-release@v2 with: tag_name: ${{ steps.version.outputs.tag_name }} name: "${{ env.APP_NAME }} ${{ steps.version.outputs.tag_name }}" body: ${{ steps.release_notes.outputs.notes || 'Release' }} draft: false prerelease: false files: app-expo/${{ env.APP_NAME }}-v${{ steps.version.outputs.version }}-web.zip env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} permissions: contents: write