MoviePilot Builder v3 #67
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: MoviePilot Builder v3 | |
| on: | |
| workflow_dispatch: | |
| push: | |
| branches: | |
| - v3 | |
| paths: | |
| - 'version.py' | |
| permissions: | |
| contents: write | |
| packages: write | |
| jobs: | |
| Docker-build: | |
| runs-on: ubuntu-latest | |
| name: Build Docker Image | |
| env: | |
| TRIVY_SKIP_DIRS: /usr/share/java | |
| TRIVY_SKIP_JAVA_DB_UPDATE: "true" | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| fetch-tags: true | |
| - name: Set up uv | |
| uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1 | |
| with: | |
| version: '0.12.5' | |
| - name: Verify dependency lock | |
| run: uv lock --check | |
| - name: Audit locked Python dependencies | |
| run: | | |
| uv export --quiet --locked --no-default-groups --group runtime-standard \ | |
| --no-emit-project --output-file /tmp/moviepilot-audit-standard.txt | |
| uvx --from pip-audit==2.10.1 pip-audit \ | |
| --require-hashes --disable-pip --strict --progress-spinner off \ | |
| --requirement /tmp/moviepilot-audit-standard.txt | |
| uv export --quiet --locked --no-default-groups --group runtime-free-threaded \ | |
| --no-emit-project --no-hashes \ | |
| --output-file /tmp/moviepilot-audit-free-threaded.txt | |
| python3 scripts/normalize_audit_requirements.py \ | |
| --lock uv.lock \ | |
| --input /tmp/moviepilot-audit-free-threaded.txt \ | |
| --output /tmp/moviepilot-audit-free-threaded-normalized.txt | |
| uvx --from pip-audit==2.10.1 pip-audit \ | |
| --no-deps --disable-pip --strict --progress-spinner off \ | |
| --requirement /tmp/moviepilot-audit-free-threaded-normalized.txt | |
| - name: Release version | |
| id: release_version | |
| run: | | |
| app_version=$(cat version.py |sed -ne "s/APP_VERSION\s=\s'v\(.*\)'/\1/gp") | |
| frontend_version=$(sed -ne "s/FRONTEND_VERSION\s*=\s*'\([^']*\)'/\1/gp" version.py) | |
| echo "app_version=$app_version" >> $GITHUB_ENV | |
| echo "SOURCE_COMMIT=$(git rev-parse HEAD)" >> $GITHUB_ENV | |
| echo "frontend_version=$frontend_version" >> "$GITHUB_OUTPUT" | |
| - name: Resolve External Payload Revisions | |
| id: payloads | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| FRONTEND_VERSION: ${{ steps.release_version.outputs.frontend_version }} | |
| run: | | |
| plugins_revision=$(git ls-remote https://github.com/jxxghp/MoviePilot-Plugins.git refs/heads/main | awk '{print $1}') | |
| resources_revision=$(git ls-remote https://github.com/jxxghp/MoviePilot-Resources.git refs/heads/main | awk '{print $1}') | |
| frontend_digest=$(gh api "repos/jxxghp/MoviePilot-Frontend/releases/tags/${FRONTEND_VERSION}" \ | |
| --jq '.assets[] | select(.name == "dist.zip") | .digest') | |
| [[ "$plugins_revision" =~ ^[0-9a-f]{40}$ ]] | |
| [[ "$resources_revision" =~ ^[0-9a-f]{40}$ ]] | |
| case "$frontend_digest" in | |
| sha256:*) frontend_sha256=${frontend_digest#sha256:} ;; | |
| *) echo "dist.zip 缺少 SHA-256 摘要" >&2; exit 1 ;; | |
| esac | |
| [[ "$frontend_sha256" =~ ^[0-9a-f]{64}$ ]] | |
| echo "plugins_revision=$plugins_revision" >> "$GITHUB_OUTPUT" | |
| echo "resources_revision=$resources_revision" >> "$GITHUB_OUTPUT" | |
| echo "frontend_digest=$frontend_digest" >> "$GITHUB_OUTPUT" | |
| echo "frontend_sha256=$frontend_sha256" >> "$GITHUB_OUTPUT" | |
| - name: Checkout Wiki Plugin Market | |
| uses: actions/checkout@v4 | |
| with: | |
| repository: jxxghp/MoviePilot-Wiki | |
| ref: main | |
| path: .build/moviepilot-wiki | |
| sparse-checkout: plugin.md | |
| sparse-checkout-cone-mode: false | |
| persist-credentials: false | |
| - name: Generate Plugin Market Default | |
| id: plugin_market | |
| run: | | |
| python3 -m scripts.generate_plugin_market_default \ | |
| --wiki-file .build/moviepilot-wiki/plugin.md \ | |
| --config-file app/runtime/config.py | |
| wiki_commit=$(git -C .build/moviepilot-wiki rev-parse HEAD) | |
| echo "wiki_commit=$wiki_commit" >> "$GITHUB_OUTPUT" | |
| - name: Download models.dev catalog | |
| id: models_catalog | |
| run: | | |
| temp_file=$(mktemp app/agent/llm/models.json.XXXXXX) | |
| trap 'rm -f "$temp_file"' EXIT | |
| curl --fail --show-error --silent --location --retry 3 \ | |
| --connect-timeout 10 --max-time 120 \ | |
| "https://models.dev/api.json" -o "$temp_file" | |
| jq -e 'type == "object"' "$temp_file" >/dev/null | |
| # Git keeps only a small placeholder; the release image receives the current catalog. | |
| chmod 644 "$temp_file" | |
| mv "$temp_file" app/agent/llm/models.json | |
| echo "digest=sha256:$(sha256sum app/agent/llm/models.json | awk '{print $1}')" >> "$GITHUB_OUTPUT" | |
| echo "Downloaded models.dev catalog ($(wc -c < app/agent/llm/models.json) bytes)" | |
| - name: Create Release Snapshot | |
| id: release_snapshot | |
| env: | |
| WIKI_COMMIT: ${{ steps.plugin_market.outputs.wiki_commit }} | |
| run: | | |
| git config user.name "github-actions[bot]" | |
| git config user.email "41898282+github-actions[bot]@users.noreply.github.com" | |
| git add app/runtime/config.py | |
| if ! git diff --cached --quiet; then | |
| git commit -m "build(plugin-market): sync default from MoviePilot-Wiki@${WIKI_COMMIT:0:12}" | |
| fi | |
| echo "release_commit=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT" | |
| - name: Docker Meta | |
| id: meta | |
| uses: docker/metadata-action@v5 | |
| with: | |
| images: | | |
| ${{ secrets.DOCKER_USERNAME }}/moviepilot-v3 | |
| ghcr.io/${{ github.repository }}-v3 | |
| tags: | | |
| type=raw,value=${{ env.app_version }} | |
| - name: Docker Meta free-threaded | |
| id: meta_ft | |
| uses: docker/metadata-action@v5 | |
| with: | |
| images: | | |
| ${{ secrets.DOCKER_USERNAME }}/moviepilot-v3t | |
| ghcr.io/${{ github.repository }}-v3t | |
| tags: | | |
| type=raw,value=${{ env.app_version }} | |
| - name: Set Up QEMU | |
| uses: docker/setup-qemu-action@v3 | |
| - name: Set Up Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Build amd64 candidate | |
| uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0 | |
| with: | |
| context: . | |
| file: docker/Dockerfile | |
| platforms: linux/amd64 | |
| load: true | |
| push: false | |
| pull: true | |
| tags: moviepilot-v3-candidate:linux-amd64 | |
| build-args: | | |
| MOVIEPILOT_FRONTEND_VERSION=${{ steps.release_version.outputs.frontend_version }} | |
| MOVIEPILOT_FRONTEND_SHA256=${{ steps.payloads.outputs.frontend_sha256 }} | |
| MOVIEPILOT_PLUGINS_REF=${{ steps.payloads.outputs.plugins_revision }} | |
| MOVIEPILOT_RESOURCES_REF=${{ steps.payloads.outputs.resources_revision }} | |
| MOVIEPILOT_PYTHON_VARIANT=standard | |
| cache-from: type=gha,scope=moviepilot-v3-standard-docker-amd64,version=2 | |
| cache-to: type=gha,scope=moviepilot-v3-standard-docker-amd64,mode=max,version=2 | |
| - name: Scan amd64 candidate vulnerabilities | |
| uses: aquasecurity/trivy-action@a9c7b0f06e461e9d4b4d1711f154ee024b8d7ab8 # v0.36.0 | |
| with: | |
| image-ref: moviepilot-v3-candidate:linux-amd64 | |
| version: v0.70.0 | |
| cache-dir: ${{ runner.temp }}/trivy | |
| scanners: vuln | |
| vuln-type: os,library | |
| severity: HIGH,CRITICAL | |
| ignore-unfixed: true | |
| trivyignores: .trivyignore.yaml | |
| exit-code: 1 | |
| - name: Build arm64 candidate | |
| uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0 | |
| with: | |
| context: . | |
| file: docker/Dockerfile | |
| platforms: linux/arm64/v8 | |
| load: true | |
| push: false | |
| pull: true | |
| tags: moviepilot-v3-candidate:linux-arm64 | |
| build-args: | | |
| MOVIEPILOT_FRONTEND_VERSION=${{ steps.release_version.outputs.frontend_version }} | |
| MOVIEPILOT_FRONTEND_SHA256=${{ steps.payloads.outputs.frontend_sha256 }} | |
| MOVIEPILOT_PLUGINS_REF=${{ steps.payloads.outputs.plugins_revision }} | |
| MOVIEPILOT_RESOURCES_REF=${{ steps.payloads.outputs.resources_revision }} | |
| MOVIEPILOT_PYTHON_VARIANT=standard | |
| cache-from: type=gha,scope=moviepilot-v3-standard-docker-arm64,version=2 | |
| cache-to: type=gha,scope=moviepilot-v3-standard-docker-arm64,mode=max,version=2 | |
| - name: Scan arm64 candidate vulnerabilities | |
| uses: aquasecurity/trivy-action@a9c7b0f06e461e9d4b4d1711f154ee024b8d7ab8 # v0.36.0 | |
| with: | |
| image-ref: moviepilot-v3-candidate:linux-arm64 | |
| version: v0.70.0 | |
| cache-dir: ${{ runner.temp }}/trivy | |
| scanners: vuln | |
| vuln-type: os,library | |
| severity: HIGH,CRITICAL | |
| ignore-unfixed: true | |
| trivyignores: .trivyignore.yaml | |
| exit-code: 1 | |
| - name: Build free-threaded amd64 candidate | |
| uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0 | |
| with: | |
| context: . | |
| file: docker/Dockerfile | |
| platforms: linux/amd64 | |
| load: true | |
| push: false | |
| pull: true | |
| tags: moviepilot-v3t-candidate:linux-amd64 | |
| build-args: | | |
| MOVIEPILOT_FRONTEND_VERSION=${{ steps.release_version.outputs.frontend_version }} | |
| MOVIEPILOT_FRONTEND_SHA256=${{ steps.payloads.outputs.frontend_sha256 }} | |
| MOVIEPILOT_PLUGINS_REF=${{ steps.payloads.outputs.plugins_revision }} | |
| MOVIEPILOT_RESOURCES_REF=${{ steps.payloads.outputs.resources_revision }} | |
| MOVIEPILOT_PYTHON_VARIANT=free-threaded | |
| cache-from: type=gha,scope=moviepilot-v3t-docker-amd64,version=2 | |
| cache-to: type=gha,scope=moviepilot-v3t-docker-amd64,mode=max,version=2 | |
| - name: Scan free-threaded amd64 candidate vulnerabilities | |
| uses: aquasecurity/trivy-action@a9c7b0f06e461e9d4b4d1711f154ee024b8d7ab8 # v0.36.0 | |
| with: | |
| image-ref: moviepilot-v3t-candidate:linux-amd64 | |
| version: v0.70.0 | |
| cache-dir: ${{ runner.temp }}/trivy | |
| scanners: vuln | |
| vuln-type: os,library | |
| severity: HIGH,CRITICAL | |
| ignore-unfixed: true | |
| trivyignores: .trivyignore.yaml | |
| exit-code: 1 | |
| - name: Build free-threaded arm64 candidate | |
| uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0 | |
| with: | |
| context: . | |
| file: docker/Dockerfile | |
| platforms: linux/arm64/v8 | |
| load: true | |
| push: false | |
| pull: true | |
| tags: moviepilot-v3t-candidate:linux-arm64 | |
| build-args: | | |
| MOVIEPILOT_FRONTEND_VERSION=${{ steps.release_version.outputs.frontend_version }} | |
| MOVIEPILOT_FRONTEND_SHA256=${{ steps.payloads.outputs.frontend_sha256 }} | |
| MOVIEPILOT_PLUGINS_REF=${{ steps.payloads.outputs.plugins_revision }} | |
| MOVIEPILOT_RESOURCES_REF=${{ steps.payloads.outputs.resources_revision }} | |
| MOVIEPILOT_PYTHON_VARIANT=free-threaded | |
| cache-from: type=gha,scope=moviepilot-v3t-docker-arm64,version=2 | |
| cache-to: type=gha,scope=moviepilot-v3t-docker-arm64,mode=max,version=2 | |
| - name: Scan free-threaded arm64 candidate vulnerabilities | |
| uses: aquasecurity/trivy-action@a9c7b0f06e461e9d4b4d1711f154ee024b8d7ab8 # v0.36.0 | |
| with: | |
| image-ref: moviepilot-v3t-candidate:linux-arm64 | |
| version: v0.70.0 | |
| cache-dir: ${{ runner.temp }}/trivy | |
| scanners: vuln | |
| vuln-type: os,library | |
| severity: HIGH,CRITICAL | |
| ignore-unfixed: true | |
| trivyignores: .trivyignore.yaml | |
| exit-code: 1 | |
| - name: Login DockerHub | |
| uses: docker/login-action@v3 | |
| with: | |
| username: ${{ secrets.DOCKER_USERNAME }} | |
| password: ${{ secrets.DOCKER_PASSWORD }} | |
| - name: Login GitHub Container Registry | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Publish multi-architecture image | |
| uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0 | |
| with: | |
| context: . | |
| file: docker/Dockerfile | |
| platforms: | | |
| linux/amd64 | |
| linux/arm64/v8 | |
| push: true | |
| pull: false | |
| tags: ${{ steps.meta.outputs.tags }} | |
| build-args: | | |
| MOVIEPILOT_FRONTEND_VERSION=${{ steps.release_version.outputs.frontend_version }} | |
| MOVIEPILOT_FRONTEND_SHA256=${{ steps.payloads.outputs.frontend_sha256 }} | |
| MOVIEPILOT_PLUGINS_REF=${{ steps.payloads.outputs.plugins_revision }} | |
| MOVIEPILOT_RESOURCES_REF=${{ steps.payloads.outputs.resources_revision }} | |
| MOVIEPILOT_PYTHON_VARIANT=standard | |
| labels: | | |
| ${{ steps.meta.outputs.labels }} | |
| org.opencontainers.image.revision=${{ steps.release_snapshot.outputs.release_commit }} | |
| org.moviepilot.source-revision=${{ env.SOURCE_COMMIT }} | |
| org.moviepilot.release-snapshot-revision=${{ steps.release_snapshot.outputs.release_commit }} | |
| org.moviepilot.frontend-version=${{ steps.release_version.outputs.frontend_version }} | |
| org.moviepilot.frontend-digest=${{ steps.payloads.outputs.frontend_digest }} | |
| org.moviepilot.plugins-revision=${{ steps.payloads.outputs.plugins_revision }} | |
| org.moviepilot.resources-revision=${{ steps.payloads.outputs.resources_revision }} | |
| org.moviepilot.plugin-market-wiki-revision=${{ steps.plugin_market.outputs.wiki_commit }} | |
| org.moviepilot.models-catalog-digest=${{ steps.models_catalog.outputs.digest }} | |
| cache-from: | | |
| type=gha,scope=moviepilot-v3-standard-docker-amd64,version=2 | |
| type=gha,scope=moviepilot-v3-standard-docker-arm64,version=2 | |
| - name: Publish free-threaded multi-architecture image | |
| uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0 | |
| with: | |
| context: . | |
| file: docker/Dockerfile | |
| platforms: | | |
| linux/amd64 | |
| linux/arm64/v8 | |
| push: true | |
| pull: false | |
| tags: ${{ steps.meta_ft.outputs.tags }} | |
| build-args: | | |
| MOVIEPILOT_FRONTEND_VERSION=${{ steps.release_version.outputs.frontend_version }} | |
| MOVIEPILOT_FRONTEND_SHA256=${{ steps.payloads.outputs.frontend_sha256 }} | |
| MOVIEPILOT_PLUGINS_REF=${{ steps.payloads.outputs.plugins_revision }} | |
| MOVIEPILOT_RESOURCES_REF=${{ steps.payloads.outputs.resources_revision }} | |
| MOVIEPILOT_PYTHON_VARIANT=free-threaded | |
| labels: | | |
| ${{ steps.meta_ft.outputs.labels }} | |
| org.opencontainers.image.revision=${{ steps.release_snapshot.outputs.release_commit }} | |
| org.moviepilot.source-revision=${{ env.SOURCE_COMMIT }} | |
| org.moviepilot.release-snapshot-revision=${{ steps.release_snapshot.outputs.release_commit }} | |
| org.moviepilot.frontend-version=${{ steps.release_version.outputs.frontend_version }} | |
| org.moviepilot.frontend-digest=${{ steps.payloads.outputs.frontend_digest }} | |
| org.moviepilot.plugins-revision=${{ steps.payloads.outputs.plugins_revision }} | |
| org.moviepilot.resources-revision=${{ steps.payloads.outputs.resources_revision }} | |
| org.moviepilot.plugin-market-wiki-revision=${{ steps.plugin_market.outputs.wiki_commit }} | |
| org.moviepilot.models-catalog-digest=${{ steps.models_catalog.outputs.digest }} | |
| cache-from: | | |
| type=gha,scope=moviepilot-v3t-docker-amd64,version=2 | |
| type=gha,scope=moviepilot-v3t-docker-arm64,version=2 | |
| - name: Promote latest image pair | |
| env: | |
| DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} | |
| run: | | |
| ghcr_repository="${GITHUB_REPOSITORY,,}" | |
| for image in \ | |
| "${DOCKER_USERNAME}/moviepilot-v3" \ | |
| "ghcr.io/${ghcr_repository}-v3"; do | |
| docker buildx imagetools create \ | |
| --tag "${image}:latest" \ | |
| "${image}:${app_version}" | |
| done | |
| for image in \ | |
| "${DOCKER_USERNAME}/moviepilot-v3t" \ | |
| "ghcr.io/${ghcr_repository}-v3t"; do | |
| docker buildx imagetools create \ | |
| --tag "${image}:latest" \ | |
| "${image}:${app_version}" | |
| done | |
| - name: Generate Changelog | |
| id: changelog | |
| run: | | |
| # 获取上一个 tag(优先 v3.*,回退到任意 v* 版本 tag) | |
| PREVIOUS_TAG=$(git tag -l 'v3.*' --sort=-v:refname | grep -v "^v${{ env.app_version }}$" | head -n 1) | |
| if [ -z "$PREVIOUS_TAG" ]; then | |
| PREVIOUS_TAG=$(git tag -l 'v[0-9]*' --sort=-v:refname | grep -v "^v${{ env.app_version }}$" | head -n 1) | |
| fi | |
| echo "Previous tag: $PREVIOUS_TAG" | |
| # 使用 || 作为分隔符,同时获取 commit 消息和作者 GitHub 用户名 | |
| if [ -z "$PREVIOUS_TAG" ]; then | |
| # 首次发布且无任何历史版本 tag,限制条数避免打印整库历史撑爆环境变量 | |
| COMMITS=$(git log --pretty=format:"%s||%an" -n 300 "${SOURCE_COMMIT}") | |
| else | |
| COMMITS=$(git log --pretty=format:"%s||%an" "${PREVIOUS_TAG}..${SOURCE_COMMIT}") | |
| fi | |
| # 分类收集 commit 消息(使用关联数组去重) | |
| declare -A SEEN | |
| FEATURES="" | |
| FIXES="" | |
| OTHERS="" | |
| while IFS= read -r line; do | |
| # 跳过空行 | |
| if [ -z "$line" ]; then | |
| continue | |
| fi | |
| # 分离 commit 消息和作者 | |
| msg=$(echo "$line" | sed 's/||[^|]*$//') | |
| author=$(echo "$line" | sed 's/.*||//') | |
| # 跳过 Merge commit 和版本更新 commit | |
| if echo "$msg" | grep -qE "^Merge pull request|^Merge branch|^更新 version"; then | |
| continue | |
| fi | |
| # 按 Conventional Commits 前缀分类 | |
| if echo "$msg" | grep -qiE "^feat(\(.+\))?:"; then | |
| desc=$(echo "$msg" | sed -E 's/^feat(\([^)]*\))?:\s*//') | |
| category="FEATURES" | |
| elif echo "$msg" | grep -qiE "^fix(\(.+\))?:"; then | |
| desc=$(echo "$msg" | sed -E 's/^fix(\([^)]*\))?:\s*//') | |
| category="FIXES" | |
| elif echo "$msg" | grep -qiE "^(docs|style|refactor|perf|test|build|ci|chore|revert)(\(.+\))?:"; then | |
| desc=$(echo "$msg" | sed -E 's/^(docs|style|refactor|perf|test|build|ci|chore|revert)(\([^)]*\))?:\s*//') | |
| category="OTHERS" | |
| else | |
| desc="$msg" | |
| category="OTHERS" | |
| fi | |
| # 使用 "分类+描述" 作为去重的 key,跳过重复内容 | |
| dedup_key="${category}::${desc}" | |
| if [ -n "${SEEN[$dedup_key]+x}" ]; then | |
| continue | |
| fi | |
| SEEN[$dedup_key]=1 | |
| # 添加 by @author 引用 | |
| entry="- ${desc} by @${author}" | |
| case "$category" in | |
| FEATURES) FEATURES="${FEATURES}${entry}\n" ;; | |
| FIXES) FIXES="${FIXES}${entry}\n" ;; | |
| OTHERS) OTHERS="${OTHERS}${entry}\n" ;; | |
| esac | |
| done <<< "$COMMITS" | |
| # 组装 changelog | |
| CHANGELOG="" | |
| if [ -n "$FEATURES" ]; then | |
| CHANGELOG="${CHANGELOG}### ✨ 新功能\n\n${FEATURES}\n" | |
| fi | |
| if [ -n "$FIXES" ]; then | |
| CHANGELOG="${CHANGELOG}### 🐛 修复\n\n${FIXES}\n" | |
| fi | |
| if [ -n "$OTHERS" ]; then | |
| CHANGELOG="${CHANGELOG}### 🔧 其他\n\n${OTHERS}\n" | |
| fi | |
| # 添加版本对比链接 | |
| if [ -n "$PREVIOUS_TAG" ]; then | |
| CHANGELOG="${CHANGELOG}**完整更新记录**: https://github.com/${{ github.repository }}/compare/${PREVIOUS_TAG}...v${{ env.app_version }}" | |
| fi | |
| # 写入环境变量 | |
| echo "CHANGELOG<<EOF" >> $GITHUB_ENV | |
| echo -e "$CHANGELOG" >> $GITHUB_ENV | |
| echo "EOF" >> $GITHUB_ENV | |
| - name: Get existing release body | |
| id: get_release_body | |
| continue-on-error: true | |
| env: | |
| CHANGELOG: ${{ env.CHANGELOG }} | |
| run: | | |
| release_body=$(curl -s -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \ | |
| "https://api.github.com/repos/${{ github.repository }}/releases/tags/v${{ env.app_version }}" | \ | |
| jq -r '.body // ""') | |
| # 如果已有手动编写的 release body,则保留;否则使用自动生成的 changelog | |
| if [ -n "$release_body" ] && [ "$release_body" != "null" ] && [ "$release_body" != "" ]; then | |
| echo "RELEASE_BODY<<EOF" >> $GITHUB_ENV | |
| echo "$release_body" >> $GITHUB_ENV | |
| echo "EOF" >> $GITHUB_ENV | |
| else | |
| echo "RELEASE_BODY<<EOF" >> $GITHUB_ENV | |
| echo "$CHANGELOG" >> $GITHUB_ENV | |
| echo "EOF" >> $GITHUB_ENV | |
| fi | |
| - name: Delete Release | |
| uses: dev-drprasad/delete-tag-and-release@v1.1 | |
| continue-on-error: true | |
| with: | |
| tag_name: v${{ env.app_version }} | |
| delete_release: true | |
| github_token: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Publish Release Tag | |
| env: | |
| RELEASE_COMMIT: ${{ steps.release_snapshot.outputs.release_commit }} | |
| run: | | |
| tag_name="v${{ env.app_version }}" | |
| if git show-ref --verify --quiet "refs/tags/${tag_name}"; then | |
| git tag -d "$tag_name" | |
| fi | |
| git tag "$tag_name" "$RELEASE_COMMIT" | |
| git push origin "refs/tags/${tag_name}" | |
| - name: Generate Release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| tag_name: v${{ env.app_version }} | |
| name: v${{ env.app_version }} | |
| body: ${{ env.RELEASE_BODY }} | |
| draft: false | |
| prerelease: false | |
| make_latest: true | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} |