Files
InfinityPacer 9e24ea0c68 ci(release): add OSV pass to dependency audit
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 16:52:54 +08:00

245 lines
11 KiB
YAML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
name: MoviePilot Builder Beta
on:
workflow_dispatch:
permissions:
contents: read
packages: write
jobs:
Docker-build:
runs-on: ubuntu-latest
name: Build Docker Image
steps:
- name: Checkout
uses: actions/checkout@v7
- name: Set up uv
uses: astral-sh/setup-uv@v10.0.1
- 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 pip-audit \
--require-hashes --disable-pip --strict --progress-spinner off \
--requirement /tmp/moviepilot-audit-standard.txt
# PyPI 服务校验哈希但公告覆盖不全(曾漏报 Mako PYSEC-2026-4243),再用 OSV 补查一遍。
uvx --from pip-audit pip-audit \
--vulnerability-service osv --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 pip-audit \
--no-deps --disable-pip --strict --progress-spinner off \
--requirement /tmp/moviepilot-audit-free-threaded-normalized.txt
uvx --from pip-audit pip-audit \
--vulnerability-service osv --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@v7
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/resources/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
# 仓库仅保留少量占位数据,测试镜像构建时下载最新模型清单。
chmod 644 "$temp_file"
mv "$temp_file" app/agent/llm/resources/models.json
echo "digest=sha256:$(sha256sum app/agent/llm/resources/models.json | awk '{print $1}')" >> "$GITHUB_OUTPUT"
echo "Downloaded models.dev catalog ($(wc -c < app/agent/llm/resources/models.json) bytes)"
- name: Resolve System Refresh Key
id: system_refresh
run: |
# 刷新键决定镜像末尾系统安全更新层何时重建:每 ISO 周最多一次,
# docker/system-baseline 变化时立即重建。计算失败只降级为基线值,不阻断发布。
baseline=$(tr -d '[:space:]' < docker/system-baseline 2>/dev/null || true)
if ! [[ "$baseline" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}$ ]]; then
echo "::warning::docker/system-baseline 缺失或格式无效,系统基线记为 unknown"
baseline=unknown
fi
week=$(date -u +%G-W%V 2>/dev/null || true)
if [[ "$week" =~ ^[0-9]{4}-W[0-9]{2}$ ]]; then
key="${week}-${baseline}"
else
echo "::warning::无法计算 ISO 周,系统刷新键仅使用系统基线"
key="${baseline}"
fi
echo "System refresh key: ${key}"
echo "baseline=${baseline}" >> "$GITHUB_OUTPUT"
echo "key=${key}" >> "$GITHUB_OUTPUT"
- name: Docker Meta
id: meta
uses: docker/metadata-action@v6
with:
images: |
${{ secrets.DOCKER_USERNAME }}/moviepilot
ghcr.io/${{ github.repository }}
tags: |
type=raw,value=beta
- name: Docker Meta free-threaded
id: meta_ft
uses: docker/metadata-action@v6
with:
images: |
${{ secrets.DOCKER_USERNAME }}/moviepilot-v3t
ghcr.io/${{ github.repository }}-v3t
tags: |
type=raw,value=beta
- name: Set Up QEMU
uses: docker/setup-qemu-action@v4
- name: Set Up Buildx
uses: docker/setup-buildx-action@v4
- name: Login DockerHub
uses: docker/login-action@v4
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Login GitHub Container Registry
uses: docker/login-action@v4
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Publish standard multi-architecture image
uses: docker/build-push-action@v7
with:
context: .
file: docker/Dockerfile
platforms: |
linux/amd64
linux/arm64/v8
push: true
pull: true
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
MOVIEPILOT_SYSTEM_REFRESH_ID=${{ steps.system_refresh.outputs.key }}
labels: |
${{ steps.meta.outputs.labels }}
org.moviepilot.system-baseline=${{ steps.system_refresh.outputs.baseline }}
org.opencontainers.image.revision=${{ env.SOURCE_COMMIT }}
org.moviepilot.source-revision=${{ env.SOURCE_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,version=2
type=gha,scope=moviepilot-v3-standard-docker-amd64,version=2
type=gha,scope=moviepilot-v3-standard-docker-arm64,version=2
cache-to: type=gha,scope=moviepilot-v3-standard-docker,mode=max,version=2
- name: Publish free-threaded multi-architecture image
uses: docker/build-push-action@v7
with:
context: .
file: docker/Dockerfile
platforms: |
linux/amd64
linux/arm64/v8
push: true
pull: true
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
MOVIEPILOT_SYSTEM_REFRESH_ID=${{ steps.system_refresh.outputs.key }}
labels: |
${{ steps.meta_ft.outputs.labels }}
org.moviepilot.system-baseline=${{ steps.system_refresh.outputs.baseline }}
org.opencontainers.image.revision=${{ env.SOURCE_COMMIT }}
org.moviepilot.source-revision=${{ env.SOURCE_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,version=2
type=gha,scope=moviepilot-v3t-docker-amd64,version=2
type=gha,scope=moviepilot-v3t-docker-arm64,version=2
cache-to: type=gha,scope=moviepilot-v3t-docker,mode=max,version=2