fix: complete open-source delivery and isolate community CI

This commit is contained in:
渡口浪人
2026-10-18 15:56:16 +08:00
parent 8aaff83b8d
commit 4e9eccd809
12 changed files with 102 additions and 12 deletions
+1 -6
View File
@@ -17,15 +17,10 @@ IMAGE_URL_ALLOWED_HOSTS=
BUILD_COMMIT=development BUILD_COMMIT=development
BUILD_TIME=unknown BUILD_TIME=unknown
# Optional read-only NAS photo mount; use deploy/docker-compose.photos.yml. # Filesystem/NFS photo sources are retired. Use Synology Photos API or WebDAV.
# NAS_PHOTOS_DIR=/volume2/photo
# PHOTO_ROOT=/photos
# Optional WebDAV credential reference; prefer a secret file under /config. # Optional WebDAV credential reference; prefer a secret file under /config.
# FRAME_FLOW_SOURCE_CLOUD_FILE=/config/webdav-cloud.secret # FRAME_FLOW_SOURCE_CLOUD_FILE=/config/webdav-cloud.secret
# Optional existing group with photo read permission; does not change NAS ACLs.
# NAS_PHOTOS_GID=101
# Optional Synology Photos shared-space people filter. Configure a pinned NAS endpoint. # Optional Synology Photos shared-space people filter. Configure a pinned NAS endpoint.
# Login in the photo-source editor; only the session is saved in /config (mode 0600). # Login in the photo-source editor; only the session is saved in /config (mode 0600).
SYNOLOGY_PHOTOS_URL= SYNOLOGY_PHOTOS_URL=
+26
View File
@@ -0,0 +1,26 @@
name: Community checks
on:
push:
branches: ["**"]
pull_request:
workflow_dispatch:
permissions:
contents: read
jobs:
verify:
# GitHub-hosted disposable runner only; never run forks on the NAS runner.
if: github.server_url == 'https://github.com'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with:
persist-credentials: false
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: '3.14'
- run: python -m pip install -e '.[test]'
- run: python -m ruff check pigway_frame_flow tests scripts
- run: python -m ruff format --check pigway_frame_flow tests scripts
- run: python -m mypy pigway_frame_flow
- run: python -m pytest -q
- run: python scripts/ci-audit.py
+1 -1
View File
@@ -8,7 +8,7 @@ on:
jobs: jobs:
verify: verify:
# The NAS Docker runner must never execute code from external forks. # The NAS Docker runner must never execute code from external forks.
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository if: github.server_url != 'https://github.com' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository)
runs-on: ubuntu-latest runs-on: ubuntu-latest
env: env:
DOCKER_PIP_INDEX_URL: ${{ vars.DOCKER_PIP_INDEX_URL }} DOCKER_PIP_INDEX_URL: ${{ vars.DOCKER_PIP_INDEX_URL }}
+19
View File
@@ -0,0 +1,19 @@
# Contributing
Use Python 3.14.x and follow the development commands in README.md. Run Ruff
lint/format checks, mypy and pytest before submitting a pull request. Keep changes
focused and include a regression test for behavior fixes where practical.
Use mock mode with device writes disabled. Do not commit `.env`, credentials,
private configuration, databases, photos, or production receipts. Tests must not
contact real frames or mutate production services.
GitHub mirrors run community checks on disposable GitHub-hosted runners, including
fork PRs. Gitea uses the existing trusted NAS runner and rejects fork PRs; maintainers
review contributions before moving them to a trusted branch. Docker build/smoke
verification remains in Gitea CI. Production releases are maintainer-controlled;
contributors do not need access to production infrastructure.
For security issues, follow SECURITY.md instead of posting exploit details publicly.
Contributions are provided under the project's MIT license; preserve third-party
licenses and provenance when introducing external code or assets.
+1
View File
@@ -37,6 +37,7 @@ RUN --mount=type=cache,id=frameflow-pip-py314-${TARGETARCH},target=/root/.cache/
else python /build-tools/install.py --locked; fi else python /build-tools/install.py --locked; fi
COPY pigway_frame_flow ./pigway_frame_flow COPY pigway_frame_flow ./pigway_frame_flow
COPY vendor ./vendor COPY vendor ./vendor
COPY LICENSE NOTICE.md ./
# Publish only the cleaned filesystem, without retaining build-stage layers. # Publish only the cleaned filesystem, without retaining build-stage layers.
RUN rm -rf /usr/local/lib/python*/site-packages/pip \ RUN rm -rf /usr/local/lib/python*/site-packages/pip \
/usr/local/lib/python*/site-packages/pip-*.dist-info \ /usr/local/lib/python*/site-packages/pip-*.dist-info \
+3 -1
View File
@@ -1,5 +1,7 @@
# PIGWay Frame Flow # PIGWay Frame Flow
Development currently supports Python 3.14.x. See [contributing](CONTRIBUTING.md) and [security reporting](SECURITY.md).
[简体中文](README.zh-CN.md) | English [简体中文](README.zh-CN.md) | English
PIGWay Frame Flow is a lightweight, self-hosted platform for programmable smart-frame automation. It runs independently on a NAS or server, with its own Web UI, image library, rendering, scheduler and REST API. KoKonna is a supported device integration; external applications do not need its credentials or SDK. PIGWay Frame Flow is a lightweight, self-hosted platform for programmable smart-frame automation. It runs independently on a NAS or server, with its own Web UI, image library, rendering, scheduler and REST API. KoKonna is a supported device integration; external applications do not need its credentials or SDK.
@@ -47,7 +49,7 @@ Synology Photos API sources work without `/photos` or NFS mounts, with shared/cu
- Seven templates: Daily Dashboard, Weather, Calendar, Quote, Photo + Text, Daily Poetry and System Alert. - Seven templates: Daily Dashboard, Weather, Calendar, Quote, Photo + Text, Daily Poetry and System Alert.
- Layout Studio for collages/posters with 1–12 images; optional OpenAI-compatible layout planning and source-driven automatic collages. - Layout Studio for collages/posters with 1–12 images; optional OpenAI-compatible layout planning and source-driven automatic collages.
- Device-bound schedules, ordered/random playlists, temporary priority overrides, restore and persistent operation history. - Device-bound schedules, ordered/random playlists, temporary priority overrides, restore and persistent operation history.
- Read-only NAS folders, Synology Photos person filtering, WebDAV and optional cloud-drive connectors. Each source has its own folder and filter settings. People filtering uses one shared Synology Photos sign-in; WebDAV and cloud-drive credentials belong to their respective sources. - Synology Photos person filtering, WebDAV and optional cloud-drive connectors. Each source has its own folder and filter settings. People filtering uses one shared Synology Photos sign-in; WebDAV and cloud-drive credentials belong to their respective sources.
- Optional weather, calendar, monitoring, AI, MQTT and webhook integrations. Provider configuration is required; an unconnected provider is not a working connection. - Optional weather, calendar, monitoring, AI, MQTT and webhook integrations. Provider configuration is required; an unconnected provider is not a working connection.
Templates **render images**, including text-only templates. Choose a template, edit its data, generate a preview, then display it. Preview generation and saving template settings do not update the frame. Photo + Text and Daily Dashboard can include a selected local photo; other templates do not automatically choose one. Scheduled template jobs render afresh on each run. See [photo sources](docs/PHOTO_SOURCES.md), [automatic collages](docs/AUTOMATIC_COLLAGE.md) and [integration setup/limits](docs/EXTENSIONS.md). Templates **render images**, including text-only templates. Choose a template, edit its data, generate a preview, then display it. Preview generation and saving template settings do not update the frame. Photo + Text and Daily Dashboard can include a selected local photo; other templates do not automatically choose one. Scheduled template jobs render afresh on each run. See [photo sources](docs/PHOTO_SOURCES.md), [automatic collages](docs/AUTOMATIC_COLLAGE.md) and [integration setup/limits](docs/EXTENSIONS.md).
+3 -1
View File
@@ -1,5 +1,7 @@
# PIGWay Frame Flow # PIGWay Frame Flow
开发环境目前支持 Python 3.14.x。参见[贡献指南](CONTRIBUTING.md)和[安全问题报告](SECURITY.md)。
简体中文 | [English](README.md) 简体中文 | [English](README.md)
轻量、自托管的智能画框自动化平台,可独立运行在 NAS 或服务器上,提供自己的 Web 界面、图库、图片渲染、调度器和 REST API。KoKonna 是支持的设备集成之一;外部应用无需持有它的凭据或引入其 SDK。 轻量、自托管的智能画框自动化平台,可独立运行在 NAS 或服务器上,提供自己的 Web 界面、图库、图片渲染、调度器和 REST API。KoKonna 是支持的设备集成之一;外部应用无需持有它的凭据或引入其 SDK。
@@ -47,7 +49,7 @@ Synology Photos 可使用独立的 API 来源,无需 `/photos` 或 NFS 挂载
- 七种模板:家庭早报、天气、日历、一句话、照片与文字、每日诗词、系统告警。 - 七种模板:家庭早报、天气、日历、一句话、照片与文字、每日诗词、系统告警。
- 拼图工作室支持 1–12 张图片的拼图与海报;可选 OpenAI 兼容接口排版,以及从数据源自动选图拼接。 - 拼图工作室支持 1–12 张图片的拼图与海报;可选 OpenAI 兼容接口排版,以及从数据源自动选图拼接。
- 绑定设备的定时任务、顺序/随机播放列表、带优先级的临时覆盖、恢复与持久化操作历史。 - 绑定设备的定时任务、顺序/随机播放列表、带优先级的临时覆盖、恢复与持久化操作历史。
- 只读 NAS 文件夹、Synology Photos 人物筛选、WebDAV 和可选网盘连接器;每个数据源独立配置、独立授权。 - Synology Photos 人物筛选、WebDAV 和可选网盘连接器;每个数据源独立配置、独立授权。
- 可选天气、日历、监控、AI、MQTT 与 Webhook 集成。需要配置对应 Provider;尚未授权的数据源不会被当作已连接。 - 可选天气、日历、监控、AI、MQTT 与 Webhook 集成。需要配置对应 Provider;尚未授权的数据源不会被当作已连接。
模板会**生成图片**,纯文字模板也是如此。使用顺序是:选择模板 → 编辑内容 → 生成预览 → 显示到画框。生成预览和保存模板设置都不会更新画框。“照片与文字”和“家庭早报”可以包含指定的本地照片,其他模板不会自动选图;定时模板任务每次运行会重新渲染。参见[照片数据源](docs/PHOTO_SOURCES.md)、[自动拼图](docs/AUTOMATIC_COLLAGE.md)和[集成配置及限制](docs/EXTENSIONS.md)。 模板会**生成图片**,纯文字模板也是如此。使用顺序是:选择模板 → 编辑内容 → 生成预览 → 显示到画框。生成预览和保存模板设置都不会更新画框。“照片与文字”和“家庭早报”可以包含指定的本地照片,其他模板不会自动选图;定时模板任务每次运行会重新渲染。参见[照片数据源](docs/PHOTO_SOURCES.md)、[自动拼图](docs/AUTOMATIC_COLLAGE.md)和[集成配置及限制](docs/EXTENSIONS.md)。
+15
View File
@@ -0,0 +1,15 @@
# Security policy
Security fixes target the latest release. Older releases have no guaranteed
backport support. Upgrade before verifying whether an issue is still present.
Do not publish credentials, private photos, production logs, or working exploit
payloads in public issues. Contact the repository owner privately through the
hosting platform when available. If no private contact channel is available,
open an issue asking for a confidential reporting channel without technical
vulnerability details. There is currently no dedicated security mailbox or
promised response SLA.
A useful private report includes the affected version, deployment mode, required
permissions, impact, and minimal steps using mock data. Do not test on another
person's deployment or write to real frames without explicit authorization.
+3 -1
View File
@@ -4,7 +4,9 @@ Runtime keys, passwords, sessions, private photos, databases and local `.env` fi
must remain outside Git. Examples use documentation-only addresses and identities. must remain outside Git. Examples use documentation-only addresses and identities.
Personal deployment reports and screenshots are excluded from public history. Personal deployment reports and screenshots are excluded from public history.
The NAS Docker runner accepts trusted repository events only. External fork PRs The Gitea NAS Docker runner accepts trusted repository events only. GitHub mirrors
use the separate community workflow on GitHub-hosted disposable runners, with
read-only repository permissions and no deployment configuration. External fork PRs
are not executed on this runner; maintainers must review them before copying changes are not executed on this runner; maintainers must review them before copying changes
to a trusted branch. All lint, type, test, audit and isolated Docker checks remain. to a trusted branch. All lint, type, test, audit and isolated Docker checks remain.
+8 -1
View File
@@ -1,4 +1,11 @@
# Gitea Actions production CD v1 # Gitea Actions production CD v1 (historical)
> This NAS production deployment procedure is retired. Current Gitea Actions
> only runs isolated verification; production uses the maintainer-specific
> Ubuntu workflow in `scripts/server-release.py`. For a new installation use
> the Docker Compose instructions in README.md. Filesystem/NFS photo sources
> are disabled; use Synology Photos API or WebDAV. The configuration below is
> retained for historical reference and must not be used for new deployments.
## Audit and adoption boundary ## Audit and adoption boundary
+3 -1
View File
@@ -1,7 +1,7 @@
[project] [project]
name = "pigway-frame-flow" name = "pigway-frame-flow"
version = "0.1.17" version = "0.1.17"
requires-python = ">=3.12" requires-python = ">=3.14,<3.15"
dependencies = ["paho-mqtt==2.1.0", "python-dateutil==2.9.0.post0", "fastapi==0.142.2", "uvicorn==0.54.0", "pillow==12.3.0", "requests==2.34.2", "croniter==6.2.4", "python-multipart==0.0.32", "pyyaml==6.0.3"] dependencies = ["paho-mqtt==2.1.0", "python-dateutil==2.9.0.post0", "fastapi==0.142.2", "uvicorn==0.54.0", "pillow==12.3.0", "requests==2.34.2", "croniter==6.2.4", "python-multipart==0.0.32", "pyyaml==6.0.3"]
[project.optional-dependencies] [project.optional-dependencies]
test = ["pytest==9.1.1", "httpx2==2.13.1", "mypy==2.3.1", "pip-audit==2.10.1", "types-requests==2.33.0.20260906", "types-PyYAML==6.0.12.20260906", "types-croniter==6.2.4.20260711", "ruff==0.16.9"] test = ["pytest==9.1.1", "httpx2==2.13.1", "mypy==2.3.1", "pip-audit==2.10.1", "types-requests==2.33.0.20260906", "types-PyYAML==6.0.12.20260906", "types-croniter==6.2.4.20260711", "ruff==0.16.9"]
@@ -9,6 +9,8 @@ test = ["pytest==9.1.1", "httpx2==2.13.1", "mypy==2.3.1", "pip-audit==2.10.1", "
pythonpath = ["."] pythonpath = ["."]
testpaths = ["tests"] testpaths = ["tests"]
[tool.ruff] [tool.ruff]
# Preserve existing syntax/formatting independently of runtime support.
target-version = "py312"
exclude = ["vendor", ".venv"] exclude = ["vendor", ".venv"]
[build-system] [build-system]
requires = ["setuptools>=75"] requires = ["setuptools>=75"]
+19
View File
@@ -34,3 +34,22 @@ def test_actions_downloads_do_not_depend_on_personal_server():
r"https://github\.com/actions/(checkout|setup-python)@[0-9a-f]{40}", r"https://github\.com/actions/(checkout|setup-python)@[0-9a-f]{40}",
action, action,
) )
def test_community_checks_are_isolated_from_private_runners():
import yaml
private = yaml.safe_load(Path(".github/workflows/verify.yml").read_text())
public = yaml.safe_load(Path(".github/workflows/community.yml").read_text())
assert (
"github.server_url != 'https://github.com'" in private["jobs"]["verify"]["if"]
)
job = public["jobs"]["verify"]
assert job["if"] == "github.server_url == 'https://github.com'"
assert job["runs-on"] == "ubuntu-latest"
assert public["permissions"] == {"contents": "read"}
workflow = Path(".github/workflows/community.yml").read_text()
assert "secrets." not in workflow
assert "production-deploy" not in workflow
assert "pull_request_target" not in workflow
assert "persist-credentials: false" in workflow