diff --git a/.github/workflows/desktop-pr-build.yml b/.github/workflows/pr-build-desktop.yml similarity index 100% rename from .github/workflows/desktop-pr-build.yml rename to .github/workflows/pr-build-desktop.yml diff --git a/.github/workflows/docker.yml b/.github/workflows/pr-build-docker.yml similarity index 69% rename from .github/workflows/docker.yml rename to .github/workflows/pr-build-docker.yml index c982c1d9b0..43e54915f1 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/pr-build-docker.yml @@ -1,33 +1,28 @@ -name: Publish Docker Image +name: Docker PR Build + +on: + pull_request: + types: [synchronize, labeled, unlabeled] # PR 更新或标签变化时触发 + +# 确保同一 PR 同一时间只运行一个相同的 workflow,取消正在进行的旧的运行 +concurrency: + group: pr-${{ github.event.pull_request.number }}-${{ github.workflow }} + cancel-in-progress: true + +# Add default permissions permissions: contents: read pull-requests: write -on: - workflow_dispatch: - release: - types: [published] - pull_request_target: - types: [synchronize, labeled, unlabeled] - -concurrency: - group: ${{ github.ref }}-${{ github.workflow }} - # PR 构建时取消旧的运行,但 release 构建不取消 - cancel-in-progress: ${{ github.event_name != 'release' }} - env: REGISTRY_IMAGE: lobehub/lobehub PR_TAG_PREFIX: pr- jobs: build: - # 添加 PR label 触发条件 - if: | - github.event_name == 'release' || - github.event_name == 'workflow_dispatch' || - (github.event_name == 'pull_request_target' && - contains(github.event.pull_request.labels.*.name, 'trigger:build-docker')) - + name: Build ${{ matrix.platform }} Docker Image + # 添加 PR label 触发条件,只有添加了 trigger:build-docker 标签的 PR 才会触发构建 + if: contains(github.event.pull_request.labels.*.name, 'trigger:build-docker') strategy: matrix: include: @@ -36,14 +31,13 @@ jobs: - platform: linux/arm64 os: ubuntu-24.04-arm runs-on: ${{ matrix.os }} - name: Build ${{ matrix.platform }} Image steps: - name: Prepare run: | platform=${{ matrix.platform }} echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV - - name: Checkout base + - name: Checkout PR branch uses: actions/checkout@v5 with: fetch-depth: 0 @@ -51,15 +45,17 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - # 为 PR 生成特殊的 tag + # 为 PR 生成特殊的 tag,使用 PR 的实际 commit SHA - name: Generate PR metadata - if: github.event_name == 'pull_request_target' id: pr_meta env: BRANCH_NAME: ${{ github.head_ref }} run: | sanitized_branch=$(echo "${BRANCH_NAME}" | sed -E 's/[^a-zA-Z0-9_.-]+/-/g') - echo "pr_tag=${sanitized_branch}-$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT + commit_sha=$(git rev-parse --short HEAD) + echo "pr_tag=${sanitized_branch}-${commit_sha}" >> $GITHUB_OUTPUT + echo "commit_sha=${commit_sha}" >> $GITHUB_OUTPUT + echo "📦 Docker Tag: ${sanitized_branch}-${commit_sha}" - name: Docker meta id: meta @@ -67,11 +63,7 @@ jobs: with: images: ${{ env.REGISTRY_IMAGE }} tags: | - # PR 构建使用特殊的 tag - type=raw,value=${{ env.PR_TAG_PREFIX }}${{ steps.pr_meta.outputs.pr_tag }},enable=${{ github.event_name == 'pull_request_target' }} - # release 构建使用版本号 - type=semver,pattern={{version}},enable=${{ github.event_name != 'pull_request_target' }} - type=raw,value=latest,enable=${{ github.event_name != 'pull_request_target' }} + type=raw,value=${{ env.PR_TAG_PREFIX }}${{ steps.pr_meta.outputs.pr_tag }} - name: Docker login uses: docker/login-action@v3 @@ -79,11 +71,6 @@ jobs: username: ${{ secrets.DOCKER_REGISTRY_USER }} password: ${{ secrets.DOCKER_REGISTRY_PASSWORD }} - - name: Get commit SHA - if: github.ref == 'refs/heads/main' - id: vars - run: echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT - - name: Build and export id: build uses: docker/build-push-action@v6 @@ -93,7 +80,7 @@ jobs: file: ./Dockerfile labels: ${{ steps.meta.outputs.labels }} build-args: | - SHA=${{ steps.vars.outputs.sha_short }} + SHA=${{ steps.pr_meta.outputs.commit_sha }} outputs: type=image,name=${{ env.REGISTRY_IMAGE }},push-by-digest=true,name-canonical=true,push=true - name: Export digest @@ -112,11 +99,13 @@ jobs: retention-days: 1 merge: - name: Merge + name: Merge and Publish needs: build runs-on: ubuntu-latest + # 只为非 fork 的 PR 发布(fork 的 PR 没有写权限) + if: github.event.pull_request.head.repo.full_name == github.repository steps: - - name: Checkout base + - name: Checkout PR branch uses: actions/checkout@v5 with: fetch-depth: 0 @@ -133,13 +122,14 @@ jobs: # 为 merge job 添加 PR metadata 生成 - name: Generate PR metadata - if: github.event_name == 'pull_request_target' id: pr_meta env: BRANCH_NAME: ${{ github.head_ref }} run: | sanitized_branch=$(echo "${BRANCH_NAME}" | sed -E 's/[^a-zA-Z0-9_.-]+/-/g') - echo "pr_tag=${sanitized_branch}-$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT + commit_sha=$(git rev-parse --short HEAD) + echo "pr_tag=${sanitized_branch}-${commit_sha}" >> $GITHUB_OUTPUT + echo "commit_sha=${commit_sha}" >> $GITHUB_OUTPUT - name: Docker meta id: meta @@ -147,9 +137,7 @@ jobs: with: images: ${{ env.REGISTRY_IMAGE }} tags: | - type=raw,value=${{ env.PR_TAG_PREFIX }}${{ steps.pr_meta.outputs.pr_tag }},enable=${{ github.event_name == 'pull_request_target' }} - type=semver,pattern={{version}},enable=${{ github.event_name != 'pull_request_target' }} - type=raw,value=latest,enable=${{ github.event_name != 'pull_request_target' }} + type=raw,value=${{ env.PR_TAG_PREFIX }}${{ steps.pr_meta.outputs.pr_tag }} - name: Docker login uses: docker/login-action@v3 @@ -168,7 +156,6 @@ jobs: docker buildx imagetools inspect ${{ env.REGISTRY_IMAGE }}:${{ steps.meta.outputs.version }} - name: Comment on PR with Docker build info - if: github.event_name == 'pull_request_target' uses: actions/github-script@v8 with: github-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/release-docker.yml b/.github/workflows/release-docker.yml new file mode 100644 index 0000000000..045b622178 --- /dev/null +++ b/.github/workflows/release-docker.yml @@ -0,0 +1,133 @@ +name: Publish Docker Image +permissions: + contents: read + +on: + workflow_dispatch: + release: + types: [published] + +concurrency: + group: ${{ github.ref }}-${{ github.workflow }} + cancel-in-progress: false + +env: + REGISTRY_IMAGE: lobehub/lobehub + +jobs: + build: + + strategy: + matrix: + include: + - platform: linux/amd64 + os: ubuntu-latest + - platform: linux/arm64 + os: ubuntu-24.04-arm + runs-on: ${{ matrix.os }} + name: Build ${{ matrix.platform }} Image + steps: + - name: Prepare + run: | + platform=${{ matrix.platform }} + echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV + + - name: Checkout base + uses: actions/checkout@v5 + with: + fetch-depth: 0 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Docker meta + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY_IMAGE }} + tags: | + type=semver,pattern={{version}} + type=raw,value=latest + + - name: Docker login + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_REGISTRY_USER }} + password: ${{ secrets.DOCKER_REGISTRY_PASSWORD }} + + - name: Get commit SHA + if: github.ref == 'refs/heads/main' + id: vars + run: echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT + + - name: Build and export + id: build + uses: docker/build-push-action@v6 + with: + platforms: ${{ matrix.platform }} + context: . + file: ./Dockerfile + labels: ${{ steps.meta.outputs.labels }} + build-args: | + SHA=${{ steps.vars.outputs.sha_short }} + outputs: type=image,name=${{ env.REGISTRY_IMAGE }},push-by-digest=true,name-canonical=true,push=true + + - name: Export digest + run: | + rm -rf /tmp/digests + mkdir -p /tmp/digests + digest="${{ steps.build.outputs.digest }}" + touch "/tmp/digests/${digest#sha256:}" + + - name: Upload artifact + uses: actions/upload-artifact@v5 + with: + name: digest-${{ env.PLATFORM_PAIR }} + path: /tmp/digests/* + if-no-files-found: error + retention-days: 1 + + merge: + name: Merge + needs: build + runs-on: ubuntu-latest + steps: + - name: Checkout base + uses: actions/checkout@v5 + with: + fetch-depth: 0 + + - name: Download digests + uses: actions/download-artifact@v6 + with: + path: /tmp/digests + pattern: digest-* + merge-multiple: true + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Docker meta + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY_IMAGE }} + tags: | + type=semver,pattern={{version}} + type=raw,value=latest + + - name: Docker login + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_REGISTRY_USER }} + password: ${{ secrets.DOCKER_REGISTRY_PASSWORD }} + + - name: Create manifest list and push + working-directory: /tmp/digests + run: | + docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ + $(printf '${{ env.REGISTRY_IMAGE }}@sha256:%s ' *) + + - name: Inspect image + run: | + docker buildx imagetools inspect ${{ env.REGISTRY_IMAGE }}:${{ steps.meta.outputs.version }}