code_packager 0.1.0

A tool to package source code files into a single text file with syntax formatting
Documentation
name: Build, Push and Release

on:
#   push:
#     tags:
#       - 'v*'  # 推送 v 开头的标签时触发
  workflow_dispatch:  # 允许手动触发
    inputs:
      debug_mode:
        description: 'Enable debug mode (no push to registry, save artifacts)'
        required: false
        default: false
        type: boolean
      use_china_mirror:
        description: 'Use China mirror for faster downloads (for China-based runners)'
        required: false
        default: false
        type: boolean
      alpine_mirror:
        description: 'Alpine Linux mirror'
        required: false
        default: 'mirrors.aliyun.com'
        type: choice
        options:
          - mirrors.aliyun.com
          - mirrors.tuna.tsinghua.edu.cn
          - mirrors.ustc.edu.cn
      rust_mirror:
        description: 'Rust Crates mirror'
        required: false
        default: 'tuna'
        type: choice
        options:
          - tuna
          - ustc

env:
  REGISTRY: ghcr.io
  BINARY_NAME: ${{ github.event.repository.name }}

jobs:
  build-and-push:
    name: Build Static Binary and Push to GHCR
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Set up flags
        id: flags
        run: |

          # 设置 debug_mode 标志
          if [ "${{ inputs.debug_mode }}" = "true" ]; then
            echo "debug_mode=true" >> $GITHUB_OUTPUT
          else
            echo "debug_mode=false" >> $GITHUB_OUTPUT
          fi
          
          # 检测是否是 main 分支
          if [ "${{ github.ref }}" = "refs/heads/main" ]; then
            echo "is_main=true" >> $GITHUB_OUTPUT
          else
            echo "is_main=false" >> $GITHUB_OUTPUT
          fi
          
          echo "debug_mode: ${{ steps.flags.outputs.debug_mode }}"
          echo "is_main: ${{ steps.flags.outputs.is_main }}"

      - name: Set up lowercase repository name
        id: repo
        run: |

          # 将仓库名称转换为小写
          REPO_LOWER=$(echo "${{ github.repository }}" | tr '[:upper:]' '[:lower:]')
          echo "repo_lower=$REPO_LOWER" >> $GITHUB_OUTPUT
          echo "Using repository: $REPO_LOWER"

      - name: Debug Information
        run: |

          echo "🔧 Debug Mode: ${{ inputs.debug_mode }}"
          echo "🌐 China Mirror: ${{ inputs.use_china_mirror }}"
          echo "📡 Alpine Mirror: ${{ inputs.alpine_mirror }}"
          echo "⚙️ Rust Mirror: ${{ inputs.rust_mirror }}"
          echo "🏷️ Trigger Event: ${{ github.event_name }}"
          echo "🔖 Ref: ${{ github.ref }}"
          echo "📝 SHA: ${{ github.sha }}"

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Log in to GitHub Container Registry (skip in debug mode)
        if: ${{ !inputs.debug_mode }}
        uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Extract Docker image metadata
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ${{ env.REGISTRY }}/${{ steps.repo.outputs.repo_lower }}
          tags: |

            type=ref,event=tag
            type=raw,value=scratch,enable=${{ steps.flags.outputs.is_main == 'true' }}
            type=raw,value=latest,enable=${{ steps.flags.outputs.is_main == 'true' }}
            type=raw,value=debug-${{ github.sha }},enable=${{ steps.flags.outputs.debug_mode == 'true' }}
            type=sha,prefix=git-

      - name: Build Docker image
        uses: docker/build-push-action@v5
        with:
          context: .
          platforms: linux/amd64
          push: ${{ !inputs.debug_mode }}  # 调试模式下不推送
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          target: runtime
          build-args: |

            USE_CHINA_MIRROR=${{ inputs.use_china_mirror }}
            ALPINE_MIRROR=${{ inputs.alpine_mirror }}
            RUST_MIRROR=${{ inputs.rust_mirror }}
          cache-from: type=gha
          cache-to: type=gha,mode=max
          outputs: ${{ inputs.debug_mode && 'type=docker,dest=/tmp/image.tar' || '' }}

      - name: Save image locally (debug mode)
        if: ${{ inputs.debug_mode }}
        run: |

          echo "🔧 Debug mode: Saving image to workspace..."
          mkdir -p /tmp/debug-artifacts
          cp /tmp/image.tar /tmp/debug-artifacts/code_packager-debug.tar
          echo "Image saved to /tmp/debug-artifacts/"

      - name: Upload debug artifacts (debug mode)
        if: ${{ inputs.debug_mode }}
        uses: actions/upload-artifact@v4
        with:
          name: docker-image-debug
          path: /tmp/debug-artifacts/
          retention-days: 1

      - name: Image inspection (debug mode)
        if: ${{ inputs.debug_mode }}
        run: |

          echo "🔍 Inspecting built image..."
          docker load -i /tmp/image.tar
          docker images
          echo "=== Image details ==="
          IMAGE_TAG="${{ env.REGISTRY }}/${{ steps.repo.outputs.repo_lower }}:debug-${{ github.sha }}"
          if docker image inspect "$IMAGE_TAG" >/dev/null 2>&1; then
            docker image inspect "$IMAGE_TAG" | jq '.[0] | {Size: .Size, Architecture: .Architecture, Os: .Os}'
          else
            echo "⚠️ Debug image not found for inspection, listing all images:"
            docker images
          fi

      - name: Test image (debug mode)
        if: ${{ inputs.debug_mode }}
        run: |

          echo "🧪 Testing built image..."
          IMAGE_TAG="${{ env.REGISTRY }}/${{ steps.repo.outputs.repo_lower }}:debug-${{ github.sha }}"
          if docker image inspect "$IMAGE_TAG" >/dev/null 2>&1; then
            docker run --rm "$IMAGE_TAG" --version || \
            docker run --rm "$IMAGE_TAG" --help || \
            echo "Image runs successfully"
            echo "✅ Image test completed"
          else
            echo "❌ Debug image not found for testing"
          fi

      - name: Extract binary from Docker image
        id: extract-binary
        run: |

          # 创建临时目录
          mkdir -p release-binaries
          
          # 使用第一个标签来提取二进制文件
          FIRST_TAG=$(echo "${{ steps.meta.outputs.tags }}" | cut -d',' -f1)
          echo "Using tag for extraction: $FIRST_TAG"
          
          # 从刚构建的镜像中提取二进制文件 - 使用正确的路径 /app/code_packager
          docker create --name extract-binary-container $FIRST_TAG
          docker cp extract-binary-container:/app/code_packager release-binaries/${{ env.BINARY_NAME }}
          docker rm -f extract-binary-container
          
          # 使二进制文件可执行
          chmod +x release-binaries/${{ env.BINARY_NAME }}
          
          # 获取二进制文件信息
          echo "binary_size=$(du -h release-binaries/${{ env.BINARY_NAME }} | cut -f1)" >> $GITHUB_OUTPUT
          echo "binary_path=release-binaries/${{ env.BINARY_NAME }}" >> $GITHUB_OUTPUT
          
          # 显示文件信息
          echo "📄 File information:"
          file release-binaries/${{ env.BINARY_NAME }}
          echo "📊 File details:"
          ls -la release-binaries/${{ env.BINARY_NAME }}
          echo "🔍 Binary inspection:"
          strings release-binaries/${{ env.BINARY_NAME }} | head -5

      - name: Test extracted binary
        run: |

          echo "🧪 Testing extracted binary..."
          ./release-binaries/${{ env.BINARY_NAME }} --version || \
          ./release-binaries/${{ env.BINARY_NAME }} --help || \
          echo "Binary executed successfully"

      - name: Upload binary artifact
        uses: actions/upload-artifact@v4
        with:
          name: ${{ env.BINARY_NAME }}-binary
          path: release-binaries/${{ env.BINARY_NAME }}
          retention-days: 1

      - name: Build completion report (production mode)
        if: ${{ !inputs.debug_mode }}
        run: |

          echo "🎉 Build Completed Successfully"
          echo "==============================="
          echo "📦 Image: ${{ env.REGISTRY }}/${{ steps.repo.outputs.repo_lower }}"
          echo "🏷️ Tags: ${{ steps.meta.outputs.tags }}"
          echo "🏗️ Architecture: x86_64 (amd64)"
          echo "📐 Platform: linux/amd64"
          echo "🔧 Build Type: Static musl binary on scratch"
          echo "📦 Binary: ${{ steps.extract-binary.outputs.binary_path }} (${{ steps.extract-binary.outputs.binary_size }})"
          echo "🌐 China Mirror: ${{ inputs.use_china_mirror }}"
          echo "📡 Alpine Mirror: ${{ inputs.alpine_mirror }}"
          echo "⚙️ Rust Mirror: ${{ inputs.rust_mirror }}"

      - name: Debug mode completion message
        if: ${{ inputs.debug_mode }}
        run: |

          echo "🔧 Debug Mode Completed Successfully!"
          echo "===================================="
          echo "📦 Image built but NOT pushed to registry"
          echo "🏷️ Debug tag: debug-${{ github.sha }}"
          echo "💾 Artifacts saved for inspection"
          echo "🔍 Image inspected and tested locally"
          echo "🔄 To publish, re-run without debug mode"

  create-release:
    name: Create GitHub Release
    runs-on: ubuntu-latest
    needs: build-and-push
    permissions:
      contents: write
    if: startsWith(github.ref, 'refs/tags/v') && !inputs.debug_mode
    
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Set up lowercase repository name
        id: repo
        run: |

          # 将仓库名称转换为小写
          REPO_LOWER=$(echo "${{ github.repository }}" | tr '[:upper:]' '[:lower:]')
          echo "repo_lower=$REPO_LOWER" >> $GITHUB_OUTPUT

      - name: Download binary artifact
        uses: actions/download-artifact@v4
        with:
          name: ${{ env.BINARY_NAME }}-binary
          path: release-binaries

      - name: Create checksums
        run: |

          cd release-binaries
          sha256sum ${{ env.BINARY_NAME }} > ${{ env.BINARY_NAME }}.sha256
          sha512sum ${{ env.BINARY_NAME }} > ${{ env.BINARY_NAME }}.sha512
          echo "Checksums created:"
          cat ${{ env.BINARY_NAME }}.sha256
          cat ${{ env.BINARY_NAME }}.sha512

      - name: Create GitHub Release
        uses: softprops/action-gh-release@v1
        with:
          files: |

            release-binaries/${{ env.BINARY_NAME }}
            release-binaries/${{ env.BINARY_NAME }}.sha256
            release-binaries/${{ env.BINARY_NAME }}.sha512
          generate_release_notes: true
          draft: false
          prerelease: ${{ contains(github.ref, '-alpha') || contains(github.ref, '-beta') || contains(github.ref, '-rc') }}
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

      - name: Release completion report
        run: |

          echo "🚀 Release Published Successfully"
          echo "================================"
          echo "📦 Binary: ${{ env.BINARY_NAME }}"
          echo "📁 Files uploaded:"
          echo "  - ${{ env.BINARY_NAME }} (executable)"
          echo "  - ${{ env.BINARY_NAME }}.sha256 (SHA256 checksum)"
          echo "  - ${{ env.BINARY_NAME }}.sha512 (SHA512 checksum)"
          echo "🏷️ Tag: ${GITHUB_REF#refs/tags/}"