From 1949a8a8881762ac14f94791d17b26b03a081546 Mon Sep 17 00:00:00 2001 From: sfun <15905889+ssfun@users.noreply.github.com> Date: Wed, 22 Jul 2026 23:36:24 +0800 Subject: [PATCH] chore(release): simplify Docker maintenance inputs --- cliproxyapi-pro-core/Dockerfile | 17 +++-------------- cliproxyapi-pro-core/Dockerfile.runtime | 9 ++------- cliproxyapi-pro-core/README.md | 1 - cliproxyapi-pro-core/README_EN.md | 1 - compatibility/upstream.env | 1 - docs/code-review/2026-07.md | 17 +++++++++++------ scripts/validation/check_reproducible_builds.py | 17 ++++++----------- 7 files changed, 22 insertions(+), 41 deletions(-) diff --git a/cliproxyapi-pro-core/Dockerfile b/cliproxyapi-pro-core/Dockerfile index 1c8b58c..45223ff 100644 --- a/cliproxyapi-pro-core/Dockerfile +++ b/cliproxyapi-pro-core/Dockerfile @@ -1,12 +1,7 @@ FROM golang:1.26-bookworm@sha256:1ecb7edf62a0408027bd5729dfd6b1b8766e578e8df93995b225dfd0944eb651 AS builder -ARG DEBIAN_SNAPSHOT=20260715T000000Z RUN set -eux; \ - sed -ri \ - -e "s#https?://deb.debian.org/debian-security#http://snapshot.debian.org/archive/debian-security/${DEBIAN_SNAPSHOT}#g" \ - -e "s#https?://deb.debian.org/debian#http://snapshot.debian.org/archive/debian/${DEBIAN_SNAPSHOT}#g" \ - /etc/apt/sources.list.d/debian.sources; \ - apt-get -o Acquire::Check-Valid-Until=false update; \ + apt-get update; \ apt-get install -y --no-install-recommends ca-certificates curl jq python3 build-essential git; \ rm -rf /var/lib/apt/lists/* @@ -59,18 +54,12 @@ RUN set -eux; \ FROM debian:bookworm@sha256:9344f8b8992482f80cba753f323adeaf17690076c095ccff6cc9536be98185dc -ARG DEBIAN_SNAPSHOT=20260715T000000Z - RUN mkdir -p /CLIProxyAPI/usage -COPY --from=ghcr.io/komari-monitor/komari-agent:latest@sha256:48815bdfce540d83c60b92809858d9aea3635d07abf78581486e0433ca97d676 /app/komari-agent /CLIProxyAPI/komari-agent +COPY --from=ghcr.io/komari-monitor/komari-agent:latest /app/komari-agent /CLIProxyAPI/komari-agent RUN set -eux; \ - sed -ri \ - -e "s#https?://deb.debian.org/debian-security#http://snapshot.debian.org/archive/debian-security/${DEBIAN_SNAPSHOT}#g" \ - -e "s#https?://deb.debian.org/debian#http://snapshot.debian.org/archive/debian/${DEBIAN_SNAPSHOT}#g" \ - /etc/apt/sources.list.d/debian.sources; \ - apt-get -o Acquire::Check-Valid-Until=false update; \ + apt-get update; \ apt-get install -y --no-install-recommends ca-certificates curl tzdata; \ rm -rf /var/lib/apt/lists/* diff --git a/cliproxyapi-pro-core/Dockerfile.runtime b/cliproxyapi-pro-core/Dockerfile.runtime index 6f67d0c..786511e 100644 --- a/cliproxyapi-pro-core/Dockerfile.runtime +++ b/cliproxyapi-pro-core/Dockerfile.runtime @@ -1,18 +1,13 @@ FROM debian:bookworm@sha256:9344f8b8992482f80cba753f323adeaf17690076c095ccff6cc9536be98185dc ARG TARGETARCH -ARG DEBIAN_SNAPSHOT=20260715T000000Z RUN mkdir -p /CLIProxyAPI/usage -COPY --from=ghcr.io/komari-monitor/komari-agent:latest@sha256:48815bdfce540d83c60b92809858d9aea3635d07abf78581486e0433ca97d676 /app/komari-agent /CLIProxyAPI/komari-agent +COPY --from=ghcr.io/komari-monitor/komari-agent:latest /app/komari-agent /CLIProxyAPI/komari-agent RUN set -eux; \ - sed -ri \ - -e "s#https?://deb.debian.org/debian-security#http://snapshot.debian.org/archive/debian-security/${DEBIAN_SNAPSHOT}#g" \ - -e "s#https?://deb.debian.org/debian#http://snapshot.debian.org/archive/debian/${DEBIAN_SNAPSHOT}#g" \ - /etc/apt/sources.list.d/debian.sources; \ - apt-get -o Acquire::Check-Valid-Until=false update; \ + apt-get update; \ apt-get install -y --no-install-recommends ca-certificates curl tzdata; \ rm -rf /var/lib/apt/lists/* diff --git a/cliproxyapi-pro-core/README.md b/cliproxyapi-pro-core/README.md index 1a17c99..7f5d7d8 100644 --- a/cliproxyapi-pro-core/README.md +++ b/cliproxyapi-pro-core/README.md @@ -245,7 +245,6 @@ docker build \ - `CLIPROXY_COMMIT` — 可选 upstream commit SHA;设置后按该提交下载源码,同时保留 `CLIPROXY_VERSION` 作为版本标识。 - `CLIPROXY_BUILD_VERSION` — 可选 runtime 版本号。为空时使用 `CLIPROXY_VERSION` 解析到的 upstream 版本。 - `SOURCE_DATE_EPOCH` — 可选 Unix 时间戳,用于写入确定的构建时间;与不可变 upstream commit 一起设置可获得确定的 source binary。 -- `DEBIAN_SNAPSHOT` — builder 和 runtime apt 源使用的 Debian snapshot 时间戳,默认固定为已审阅的 `20260715T000000Z`。 - `GITHUB_TOKEN` — 可选 GitHub API token。 Release workflow 会从 Core、models 和定制层三个不可变提交中取最新时间作为 `SOURCE_DATE_EPOCH`。Core 归档统一规范文件顺序、时间戳、属主和权限,Go 构建同时启用 `-trimpath`。 diff --git a/cliproxyapi-pro-core/README_EN.md b/cliproxyapi-pro-core/README_EN.md index 0be2db4..272fb86 100644 --- a/cliproxyapi-pro-core/README_EN.md +++ b/cliproxyapi-pro-core/README_EN.md @@ -230,7 +230,6 @@ Build args: - `CLIPROXY_COMMIT` — optional upstream commit SHA; when set, source is downloaded from that commit while `CLIPROXY_VERSION` remains the version label. - `CLIPROXY_BUILD_VERSION` — optional runtime version. If empty, it uses the upstream version resolved from `CLIPROXY_VERSION`. - `SOURCE_DATE_EPOCH` — optional Unix timestamp used for the embedded build date. Set it together with an immutable upstream commit for a deterministic source binary. -- `DEBIAN_SNAPSHOT` — Debian snapshot timestamp used by builder and runtime apt sources; defaults to the reviewed `20260715T000000Z` snapshot. - `GITHUB_TOKEN` — optional token for GitHub API requests. Release workflows derive `SOURCE_DATE_EPOCH` from the newest immutable Core, models, and customization commit. Core archives use normalized ordering, timestamps, ownership, and permissions; Go builds also use `-trimpath`. diff --git a/compatibility/upstream.env b/compatibility/upstream.env index 6b44b51..d404109 100644 --- a/compatibility/upstream.env +++ b/compatibility/upstream.env @@ -3,4 +3,3 @@ CORE_UPSTREAM_TAG=v7.2.94 MANAGEMENT_UPSTREAM_REPO=router-for-me/Cli-Proxy-API-Management-Center MANAGEMENT_UPSTREAM_TAG=v1.18.5 BUN_VERSION=1.3.14 -DEBIAN_SNAPSHOT=20260715T000000Z diff --git a/docs/code-review/2026-07.md b/docs/code-review/2026-07.md index 01f7990..33e3ea5 100644 --- a/docs/code-review/2026-07.md +++ b/docs/code-review/2026-07.md @@ -49,8 +49,8 @@ The pinned PR baseline lives in `compatibility/upstream.env`. Pull requests and | PATCH-006 | P2 | Management overlay provenance | The reviewed replacement allowlist bound only upstream hashes, so local full-file replacement content could change without updating the review metadata | A customization-only edit could bypass the explicit replacement review boundary while the upstream file stayed unchanged | Modify a reviewed overlay replacement without changing its upstream counterpart | Move replacement provenance to a versioned manifest that binds the upstream tag and SHA-256 plus the overlay SHA-256, and reject changes on either side | Overlay collision source-hash test and repository manifest validation | Resolved | | RELEASE-001 | P1 | GitHub Actions | Workflow actions used mutable major-version tags, including actions with release and workflow-run permissions | A moved tag could execute unreviewed code with repository or registry credentials | Inspect every external `uses:` reference | Pin all actions to commit SHAs and enable grouped Dependabot updates | Workflow pin checker and actionlint | Resolved | | RELEASE-002 | P1 | Release inputs | Matrix jobs resolved upstream tags and the models branch independently | One release could combine different source or model commits across platforms | Compare the checkout and models fetch inputs used by matrix jobs | Resolve Core, Management, and models commits once and pass immutable SHAs to every consumer | Workflow validation and release notes mapping | Resolved | -| RELEASE-003 | P1 | Build dependencies | Container bases, Komari executable image, manylinux builders, and downloaded Go archives were not content-verified | Rebuilding the same commit could execute different build or runtime inputs | Inspect Docker `FROM`/`COPY --from` and toolchain download steps | Pin image digests and verify the official Go archive SHA-256 | Source-image build and workflow validation | Resolved | -| RELEASE-004 | P2 | Core build reproducibility | Docker apt installs used rolling repositories, binaries embedded the current wall-clock time and local source paths, and platform archive tools retained host metadata | Rebuilding the same immutable source inputs could change the runtime dependency set, binary metadata, or release archive bytes | Rebuild after repository metadata, clock, or checkout path changes and compare artifact inputs and hashes | Pin a reviewed Debian snapshot, derive one `SOURCE_DATE_EPOCH` from immutable Core/models/customization commits, fix generated commit dates, use `-trimpath`, and normalize every tar.gz/zip entry | Reproducible archive tests, static workflow/Docker guards, pinned source-image build, and no-cache image-boundary audit | Mitigated | +| RELEASE-003 | P1 | Build dependencies | Container bases, the Komari executable image, manylinux builders, and downloaded Go archives were not content-verified | Rebuilding the same commit could execute different build or runtime inputs | Inspect Docker `FROM`/`COPY --from` and toolchain download steps | Pin base and build images, verify downloaded Go archives, and explicitly trust the official Komari `latest` image as a maintenance-first runtime dependency | Source-image build and workflow validation | Accepted upstream trust boundary | +| RELEASE-004 | P2 | Core build reproducibility | Rolling system repositories, hosted toolchains, and OCI exporters prevent a practical cross-time byte-for-byte guarantee | Rebuilding the same immutable source can receive newer system packages or exporter metadata, while official published artifacts remain authoritative | Rebuild after repository, runner-image, or exporter updates and compare artifact inputs and hashes | Keep immutable source inputs, deterministic build metadata, `-trimpath`, and normalized archives; deliberately allow rolling Debian packages and external toolchains | Reproducible archive tests, static workflow/Docker guards, and source-image build | Accepted maintenance boundary | | STATE-001 | P1 | Runtime state queue | Failed SQLite writes were removed from the writer's pending maps, and snapshots were silently dropped when the channel filled | Routing cursors or account runtime statistics could lag permanently or disappear across export/restart | Force an auth-runtime insert failure or hold SQLite while queuing more than 1024 snapshots | Retain failed items, coalesce overflow by key, drain overflow before flush/delete/stop, and retry during shutdown | Runtime writer failure and overflow tests | Resolved | | STATE-002 | P1 | Usage collection lifecycle | `PopOldest` removed usage payloads before SQLite commit, and service shutdown closed the store without waiting for workers | A transient write error or graceful restart could lose an already-popped batch or race an in-flight store operation | Fail `usage_events` inserts after enqueue, then recover SQLite; cancel the service during background work | Keep the popped batch pending until commit, retry it, flush pending events on shutdown, and wait for workers before closing SQLite | Collector retry test and race detector | Resolved | | STATE-003 | P1 | Reset boundary | A delayed live batch could commit after reset and repopulate statistics with pre-reset requests | Reset appeared to succeed, then old requests reappeared and stale SSE cursors advanced | Reset after an event is queued or popped but before its insert transaction | Serialize live inserts with reset/retention and reject live events whose timestamps are at or before the persisted reset boundary | Live-event reset-boundary test and SSE reset tests | Resolved | @@ -77,6 +77,11 @@ The pinned PR baseline lives in `compatibility/upstream.env`. Pull requests and | FRONTEND-009 | P2 | Realtime log search and ownership | Account-name search derived auth indexes only from recently loaded event rows and then suppressed raw event-text search; request cancellation, stable cursors, auto-follow, and scroll restoration all remained embedded in the page | Older events for a known account could be omitted, raw matches could disappear when account metadata also matched, and further paging changes had a large regression surface | Search for an account absent from the recent event snapshot while another event contains the same raw term, then paginate the combined results | Build auth matches from complete auth-file metadata, OR them with raw text on the server, preserve both conditions in the stable cursor, and extract the realtime controller into a dedicated hook | Combined-search cursor test, auth metadata search tests, controller source guards, full Core/Management validation, and race detector | Resolved | | FRONTEND-010 | P2 | Analytics compatibility fallback | Monitoring retains client-side analytics when `/usage/aggregates` is unavailable, while retained event details are intentionally bounded | On older servers, analytics can be approximate when more than 6000 candidate rows are retained | Run Management against a server without the aggregate endpoint and exceed `ACCOUNT_STATS_ANALYTICS_ROW_LIMIT` | Prefer server aggregates, cap fallback work at 6000 rows, and retire the fallback only when the compatibility baseline enforces aggregate API support | Aggregate and fallback analytics Bun tests plus full Management validation | Accepted compatibility boundary | +## Accepted Release Boundaries + +- `RELEASE-003` owner: project maintainers. The official Komari `latest` image is trusted as an upstream runtime component so updates arrive without digest-maintenance PRs; pinned base/build images and verified Go archives remain in place. +- `RELEASE-004` owner: project maintainers. Rolling Debian security packages and hosted toolchains are accepted because lower maintenance and timely system updates matter more than cross-time byte-for-byte reproduction; deterministic source identity, build metadata, and release archives remain enforced. + ## Package 12 Residual Risks - `AccountInspectionPage.tsx` is now 2211 lines, down from 3493 (about 37%). It intentionally remains the page-level coordinator for transport lifecycle, destructive-action commands, settings persistence, and rendered composition; pure result derivation, account statistics, confirmation builders, and reducer state now live in the feature model. @@ -125,18 +130,18 @@ The pinned PR baseline lives in `compatibility/upstream.env`. Pull requests and - Hosted runner images and their macOS, Windows, and Ubuntu compilers, linkers, system libraries, Python, zlib, and other tools remain externally versioned inputs. Pinned actions and immutable source inputs narrow this boundary; byte-for-byte Core binary and archive comparisons are meaningful only under the same reviewed toolchain environment. - The FreeBSD cross-build action still installs `clang`, `lld`, and `wget` from the current Ubuntu runner repositories. Its action revision is pinned and Core metadata/archives are normalized, but compiler and linker package versions can still drift. -- Docker image dependency inputs and application files are pinned, but Docker/BuildKit layer tar metadata remains exporter-dependent. Independent no-cache builds produced identical application file hashes and visible filesystem metadata but different image IDs; consumers must verify the digest published for each release rather than assume a locally rebuilt OCI digest will match byte-for-byte. -- The Debian snapshot must be advanced deliberately to receive security fixes. `DEBIAN_SNAPSHOT` is centralized in the compatibility baseline and validated against every Docker stage so the update is explicit and reviewable rather than silent. +- Base and manylinux images remain pinned, while Debian packages and the official Komari `latest` image intentionally follow upstream updates. This reduces pin maintenance but means rebuilding an unchanged commit at a later date can consume different runtime inputs. +- Docker/BuildKit layer tar metadata remains exporter-dependent. Consumers must verify the digest published for each release rather than assume a locally rebuilt OCI digest will match byte-for-byte. - Local Docker builds that intentionally resolve `latest` and omit `CLIPROXY_COMMIT` or `SOURCE_DATE_EPOCH` remain dynamic. Deterministic source binaries require the documented immutable commit and epoch inputs used by CI/release paths. - Exact-string patch anchors and reviewed full-file replacements remain manual merge boundaries when upstream changes. Both now fail closed before writes; the manifest binds changes on either side, but semantic reconciliation still requires human review. ## Package 9 Validation Results - Repository validation: 20 Python customization tests, two reproducible archive tests, immutable workflow action checks, Python/shell syntax, JSON, YAML, and whitespace checks passed. Local ShellCheck and actionlint executables were unavailable; required PR checks provide those CI gates. -- The pinned source Docker build for Core `v7.2.94` and commit `36b45d57a3e804b9dfcee307e5d7b3e8cea5acfc` completed through both builder and runtime snapshot apt installs and produced the final image with a fixed build date and `-trimpath`. +- The source Docker build for pinned Core `v7.2.94` and commit `36b45d57a3e804b9dfcee307e5d7b3e8cea5acfc` completed through both builder and runtime apt installs and produced the final image with a fixed build date and `-trimpath`. - Core `v7.2.94`: guarded-source preflight, rejected reapplication invariance, embedded usage tests, management handlers, plugin host/store, Redis queue, auth scheduler tests, the embedded usage and targeted package race detector, and the server build passed on a fresh generated checkout. - Management `v1.18.5`: bidirectional overlay replacement preflight, repeated-application invariance, 91 Bun tests, ESLint with no warnings, TypeScript checking, and the production single-file build passed on a fresh checkout. -- Regression coverage verifies deterministic tar.gz/zip ordering and metadata, repository-wide use of fixed Core build inputs, Debian snapshot pinning in every Docker stage, manifest baseline alignment, and rejection when either a reviewed upstream replacement or its overlay source changes without a manifest update. +- Regression coverage verifies deterministic tar.gz/zip ordering and metadata, repository-wide use of fixed Core build inputs, the accepted Docker maintenance policy, manifest baseline alignment, and rejection when either a reviewed upstream replacement or its overlay source changes without a manifest update. ## Package 8 Residual Risks diff --git a/scripts/validation/check_reproducible_builds.py b/scripts/validation/check_reproducible_builds.py index 132b7de..0c97746 100644 --- a/scripts/validation/check_reproducible_builds.py +++ b/scripts/validation/check_reproducible_builds.py @@ -9,13 +9,11 @@ def require(text: str, needle: str, source: Path) -> None: raise SystemExit(f'{source}: missing reproducible-build guard: {needle}') -def check_dockerfile(path: Path, expected_stages: int, snapshot: str) -> None: +def check_dockerfile_maintenance_policy(path: Path) -> None: text = path.read_text() - if text.count(f'ARG DEBIAN_SNAPSHOT={snapshot}') != expected_stages: - raise SystemExit(f'{path}: expected {expected_stages} pinned Debian snapshot stage(s)') - require(text, 'http://snapshot.debian.org/archive/debian/${DEBIAN_SNAPSHOT}', path) - require(text, 'http://snapshot.debian.org/archive/debian-security/${DEBIAN_SNAPSHOT}', path) - require(text, 'Acquire::Check-Valid-Until=false', path) + for forbidden in ('DEBIAN_SNAPSHOT', 'snapshot.debian.org', 'komari-agent:latest@sha256:'): + if forbidden in text: + raise SystemExit(f'{path}: retired high-maintenance Docker pin remains: {forbidden}') def main() -> None: @@ -27,15 +25,12 @@ def main() -> None: for line in (root / 'compatibility/upstream.env').read_text().splitlines() if line and not line.startswith('#') and '=' in line ) - snapshot = compatibility.get('DEBIAN_SNAPSHOT', '') - if not snapshot: - raise SystemExit('compatibility/upstream.env: DEBIAN_SNAPSHOT is required') overlay_manifest_path = root / 'cliproxyapi-pro-management/overlay-replacements.json' overlay_manifest = json.loads(overlay_manifest_path.read_text()) if overlay_manifest.get('upstream', {}).get('tag') != compatibility.get('MANAGEMENT_UPSTREAM_TAG'): raise SystemExit(f'{overlay_manifest_path}: upstream tag must match compatibility/upstream.env') - check_dockerfile(root / 'cliproxyapi-pro-core/Dockerfile', 2, snapshot) - check_dockerfile(root / 'cliproxyapi-pro-core/Dockerfile.runtime', 1, snapshot) + check_dockerfile_maintenance_policy(root / 'cliproxyapi-pro-core/Dockerfile') + check_dockerfile_maintenance_policy(root / 'cliproxyapi-pro-core/Dockerfile.runtime') ci_path = root / '.github/workflows/ci.yml' ci = ci_path.read_text()