Merge upstream main into fix/issue-283-color-scheme-echo

This commit is contained in:
GeekMr committed 2026-09-28 09:19:49 +08:00
commit 1cd74f2963
7 files changed
+232 -65

No files matched your search

+6 -2
View File
@@ -20,6 +20,9 @@ inputs:
save-if:
description: Allow publishing caches; PR consumers can restore without competing with the default branch
default: "true"
restore-downloads:
description: Restore and populate shared downloads; disable only when an earlier call in this job already restored them
default: "true"
outputs:
cache-key:
@@ -90,6 +93,7 @@ runs:
PY
- name: Restore shared Cargo downloads
id: downloads
if: inputs.restore-downloads == 'true'
uses: actions/cache/restore@v4
with:
# Preserve Git source checkouts with their mtimes. Recreating them from
@@ -173,7 +177,7 @@ runs:
echo "legacy-fallback-outcome=$legacy_outcome"
} >> "$GITHUB_OUTPUT"
- name: Populate complete downloads before sharing the cache
if: inputs.save-if == 'true' && steps.downloads.outputs.cache-hit != 'true'
if: inputs.save-if == 'true' && steps.downloads.outputs.cache-hit != 'true' && inputs.restore-downloads == 'true'
shell: bash
env:
CACHE_OS: ${{ runner.os }}
@@ -193,7 +197,7 @@ runs:
;;
esac
- name: Save complete shared Cargo downloads
if: inputs.save-if == 'true' && steps.downloads.outputs.cache-hit != 'true'
if: inputs.save-if == 'true' && steps.downloads.outputs.cache-hit != 'true' && inputs.restore-downloads == 'true'
uses: actions/cache/save@v4
with:
path: |
+38 -50
View File
@@ -11,7 +11,7 @@ on:
push:
branches: [main]
# Include docs, catalogs, assets and workflow edits: every PR gets test results.
# 草稿已跑完整矩阵,转为 Ready 而没有新提交时不重复申请七个原生 runner。
# 草稿已跑完整矩阵,转为 Ready 而没有新提交时不重复申请五个原生 runner。
pull_request:
types: [opened, synchronize, reopened]
merge_group:
@@ -145,6 +145,18 @@ jobs:
# PR merge-ref 缓存不与其他 PR 共享;只由默认分支维护可复用的依赖快照。
revision: dependencies-v1
save-if: ${{ github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }}
# 构建前计算两类缓存身份,避免生成文件进入 hashFiles 后改变原有缓存键。
# release 保留独立产物路径,但不重复恢复同一份源码下载。
- name: Restore macOS release workload
if: ${{ !cancelled() && runner.os == 'macOS' }}
uses: ./.github/actions/rust-cache
id: macos-release-cache
with:
key: native-release-check
target: target/release
revision: dependencies-v1
save-if: ${{ github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }}
restore-downloads: "false"
- name: Prepare pinned Windows console runtime
if: ${{ runner.os == 'Windows' }}
shell: pwsh
@@ -163,6 +175,7 @@ jobs:
- name: Check the release workspace
if: ${{ !cancelled() && runner.os != 'macOS' }}
run: cargo check --locked --workspace --release
# Mac 原生快照先于 release 检查保存,避免后者失败或取消丢掉已验证的编译结果。
- name: Save compiled workload
if: ${{ success() && steps.rust-cache.outputs.save-enabled == 'true' && steps.rust-cache.outputs.cache-hit != 'true' }}
uses: actions/cache/save@v4
@@ -171,6 +184,15 @@ jobs:
target/ci
${{ runner.os != 'macOS' && 'target/release' || '' }}
key: ${{ steps.rust-cache.outputs.cache-key }}
- name: Check macOS release workspace
if: ${{ !cancelled() && runner.os == 'macOS' }}
run: cargo check --locked --workspace --release --timings
- name: Save macOS release workload
if: ${{ success() && steps.macos-release-cache.outputs.save-enabled == 'true' && steps.macos-release-cache.outputs.cache-hit != 'true' }}
uses: actions/cache/save@v4
with:
path: target/release
key: ${{ steps.macos-release-cache.outputs.cache-key }}
- name: Upload native compilation timings
if: ${{ always() }}
uses: actions/upload-artifact@v4
@@ -181,60 +203,26 @@ jobs:
compression-level: 0
retention-days: 14
# The release check has no dependency on the test executable. Keeping it in
# the Intel test job added 11m33s after a 22m29s test/compile step in run
# 34856491552. Both jobs remain part of this reusable workflow's result, so
# the stable release aggregation still requires every check to succeed.
# 保留服务端已有的必需检查名称,但不为结果汇总再申请稀缺的 macOS runner。
# 只接受本轮完整原生矩阵成功;上游失败或跳过时不能把汇总直接标绿。
macos-release-check:
needs: lint
if: ${{ inputs.windows_diagnostics_run == '' }}
needs: [lint, native-tests]
if: ${{ !cancelled() && inputs.windows_diagnostics_run == '' }}
name: Release workspace (${{ matrix.os }})
strategy:
fail-fast: false
matrix:
include: ${{ fromJSON(needs.lint.outputs.release_matrix) }}
runs-on: ${{ matrix.os }}
timeout-minutes: 120
env:
PYTHONUTF8: "1"
CARGO_TERM_COLOR: always
RUST_BACKTRACE: "1"
RUST_TOOLCHAIN: "1.97.1"
runs-on: ubuntu-24.04
timeout-minutes: 5
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- uses: actions/setup-python@v5
with:
python-version: "3.13"
- name: Require the AppKit SDK used by this PR
- name: Require this run's native and release checks
shell: python
env:
NATIVE_RESULT: ${{ needs.native-tests.result }}
run: |
sdk_version="$(xcrun --sdk macosx --show-sdk-version)"
test "${sdk_version%%.*}" -ge 26
- uses: dtolnay/rust-toolchain@stable
with:
toolchain: "1.97.1"
- uses: ./.github/actions/rust-cache
id: rust-cache
with:
key: native-release-check
target: target/release
revision: dependencies-v1
save-if: ${{ github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }}
- name: Check the release workspace
run: cargo check --locked --workspace --release --timings
- name: Save compiled workload
if: ${{ success() && steps.rust-cache.outputs.save-enabled == 'true' && steps.rust-cache.outputs.cache-hit != 'true' }}
uses: actions/cache/save@v4
with:
path: target/release
key: ${{ steps.rust-cache.outputs.cache-key }}
- name: Upload release check compilation timings
if: ${{ always() }}
uses: actions/upload-artifact@v4
with:
name: release-check-timings-${{ matrix.os }}
path: target/cargo-timings
if-no-files-found: warn
compression-level: 0
retention-days: 14
import os
result = os.environ["NATIVE_RESULT"]
if result != "success":
raise SystemExit("Native validation did not succeed: " + result)
print("Both macOS release checks passed in the native matrix.")
+8 -2
View File
@@ -131,7 +131,13 @@ compile check into a claim that UI tests or a packaged application were run.
on `main`, together with Code Owner approval; see the
[activation checklist](docs/project-constraints.md#server-side-activation).
Draft and ready pull requests both run all five native platforms and both macOS
release-profile compile checks. The required lint job validates the event and
release-profile compile checks. Each macOS native job also runs its release check
on the same runner, reusing the checkout, toolchain and source downloads while
keeping the two compiled workloads in their existing separate caches. The two
`Release workspace (<os>)` contexts are lightweight Linux result checks: they
require this run's entire native matrix to succeed, not a previous run's result.
The release compilation itself still runs natively on each Mac architecture.
The required lint job validates the event and
selects the matrix before requesting platform runners. Every required check must
succeed before merging. Marking a draft ready without changing its commits does
not repeat the matrix; source updates and reopen events run it again. Native CI
@@ -155,7 +161,7 @@ feature work; there is no routine `--skip-architecture` option.
- 先读架构图、工程合同和决策记录;按职责拆分,不按行号切片。
- 一个 PR 只做一件事;改动超过 1500 行源码(不计文档、lockfile、资源)`pr-size` 会失败,请拆分。
- Draft 和 Ready PR 都先运行必需的格式检查和矩阵规划,再执行五平台原生测试及两项 macOS release 编译检查;十项必需检查全绿才能合并。提速使用共享缓存与并行测试,不省略平台或 doctest。
- Draft 和 Ready PR 都先运行必需的格式检查和矩阵规划,再执行五平台原生测试及两项 macOS release 编译检查;每个 Mac 在同一 runner 完成两类检查,原有 release 检查名称由轻量结果汇总保留。十项必需检查全绿才能合并,不省略平台或 doctest。
- 2000 行是现有仓库的防灾上限,800 行只提示审查,不是“大厂标准”。
- 普通功能 PR 不得增加存量债务;有问题的规则可以修订,但要有反例、测试和维护者审批。
- 新增核心抽象、依赖方向、持久化或线程模型改变要先说明设计,不强迫每个小修复写 ADR。
@@ -1,5 +1,8 @@
# Speed up native PR checks without dropping platform coverage
Independent macOS runner placement superseded by
[shared macOS runners](2026-09-27-shared-macos-runners.md).
## Status
Implemented for review. The five native check names and two macOS release-check
@@ -0,0 +1,105 @@
# Share macOS runners without dropping native or release checks
## Status
Implemented for review. Required check names and coverage are preserved;
GitHub protection settings are unchanged. Hosted execution and timing of this
revision remain to be measured.
## Context
Separating each Mac's native tests and release-profile compile check requests
four macOS runners per PR. Parallel work can shorten execution, but an additional
runner request can also become the critical path when capacity is scarce.
## Evidence
In [run 36314579470](https://github.com/Kuddev/pebrel/actions/runs/36314579470),
the Intel release check executed for 3m16s after waiting 60m09s. The workflow
finished after 66m21s. This is a scheduling problem, not an hour of compilation.
That run's Windows ARM64 job missed its compiled target cache at 11:09:51 UTC.
The same key's currently available default-branch entry was created at 11:31:52
UTC on 2026-09-27. This establishes availability at those two observations,
not the cause of any earlier eviction or a broken cache key. Do not invalidate
existing caches as a speculative fix.
GitHub's [cache documentation](https://docs.github.com/en/actions/reference/workflows-and-actions/dependency-caching)
states that the cached paths participate in cache versioning. Combining the
two target-directory lists would prevent reuse even if the visible key stayed
unchanged. Its [job dependency contract](https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-syntax#jobsjob_idneeds)
also requires an explicit status condition to continue after a failed dependency.
## Decision
- Run the original `cargo check --locked --workspace --release --timings` after
each Mac's native suite on the same runner. Retain the native architecture,
AppKit SDK requirement, pinned toolchain, test features and profile settings.
Linux and both Windows jobs retain their existing release checks.
- Keep both `Release workspace (<os>)` names as short Ubuntu jobs. They depend
on lint and the current native matrix, and reject any result other than exact
`success`. They do not run Mac compilation on Linux, read older check results,
upload fabricated statuses, or require write permissions.
- Preserve separate `native` and `native-release-check` cache keys, generations
and target paths. Both identities are computed before build/test commands can
generate additional manifests. The second cache action reuses downloads already
restored by the first action; other callers keep the default download behavior.
- Save the fully successful native workload before beginning the Mac release
workload. A later release failure or cancellation need not discard that
completed warmup. Neither incomplete workload nor read-only PR consumers may
publish a compiled snapshot.
- Retain all five platforms for draft/ready PRs and merge groups, both Python
suites, nextest, doctests, the product feature check and current cancellation
policy. Release/package callers and branch protection are not redesigned.
## Rejected alternatives
- Remove a platform, release compilation, doctests or assertions: changes the
required coverage rather than removing scheduling waste.
- Keep requesting a separate Mac just to preserve a status name: a current-run
dependency check retains the required context without another scarce runner.
- Introduce per-platform result artifacts or custom check-writing credentials:
unnecessary for the conservative rule that every native job must succeed.
- Combine existing target archives or bump their generation: causes a cold-cache
transition without changing any compiler inputs or fixing a proven defect.
- Delete remote caches or increase storage quota: not needed for this runner
change, and not evidence that cache retention has been solved.
## Consequences
Each full invocation requests two rather than four macOS runners. The two short
result jobs still request Ubuntu runners, so this is not a reduction in the
number of named checks. Each Mac's execution becomes sequential and can take
longer on a cold cache; the change targets additional runner queue time.
Both release contexts wait for the entire native matrix. An unrelated native
failure also fails these summaries; the per-platform job logs retain the actual
compile outcome. This is conservative and does not weaken merge acceptance.
Native timing artifacts now include the Mac release compilation timings too.
Existing cache footprint, dependency cold builds and default-branch seed timing
remain relevant. This change does not promise a fixed CI duration or eliminate
Windows ARM64 compilation cost.
## Validation
The six existing lint contract suites ran 62 tests on Windows: 61 passed and the
existing POSIX-only cache-summary case was skipped. The workflow's actual Python
result-check body accepts `success` and rejects failure, cancellation, skipped,
empty and malformed results. Contracts retain full platform/event coverage,
release compilation, default-branch-only cache publication and independent
cache identities. Actionlint 1.7.12, workspace rustfmt, architecture checking
against the PR base and the platform budget passed. Hosted cache restore/save
and cross-platform timings remain pending a real workflow run.
## Supersedes
Only the separate macOS runner placement in
[shared native PR caches](2026-09-25-shared-native-pr-cache.md). Its coverage,
cache ownership and native test-runner decisions remain in force.
## Revisit when
Hosted measurements show sequential cold compilation exceeds the queue savings,
macOS capacity changes, or a demonstrated cache-retention issue warrants its own
bounded change. Compare queue, active execution and cache hit status separately.
+12
View File
@@ -195,6 +195,18 @@ class CacheIdentityTests(unittest.TestCase):
body = source.split(f"- name: {step}", 1)[1].split("\n - name:", 1)[0]
self.assertIn("if: inputs.save-if == 'true' && steps.downloads.outputs.cache-hit != 'true'", body)
def test_second_workload_can_reuse_downloads_without_restoring_or_saving_them_again(self):
source = self.action_text()
option = source.split("\n restore-downloads:\n", 1)[1].split("\noutputs:", 1)[0]
self.assertIn('default: "true"', option)
for step in ("Restore shared Cargo downloads", "Populate complete downloads before sharing the cache",
"Save complete shared Cargo downloads"):
body = source.split(f"- name: {step}", 1)[1].split("\n - name:", 1)[0]
condition = next(line for line in body.splitlines() if line.strip().startswith("if:"))
self.assertIn("inputs.restore-downloads == 'true'", condition)
target = source.split("- name: Restore compiled targets", 1)[1].split("\n - name:", 1)[0]
self.assertNotIn("restore-downloads", target)
@unittest.skipUnless(os.name == "nt", "Windows cache migration")
def test_windows_migration_preserves_compiler_and_workload_boundaries(self):
before = self.identity()
+60 -11
View File
@@ -1,8 +1,11 @@
from __future__ import annotations
import os
import subprocess
import re
from pathlib import Path
import sys
import textwrap
import tomllib
import unittest
from unittest.mock import patch
@@ -43,11 +46,13 @@ class NativeSuiteTests(unittest.TestCase):
platform_step = lint.split("Check platform cfg budget before native jobs", 1)[1].split(" - name:", 1)[0]
self.assertNotIn("continue-on-error", platform_step)
self.assertNotIn("--update", platform_step)
for job, output in (("native-tests", "native_matrix"),
("macos-release-check", "release_matrix")):
for job, output, needs in (
("native-tests", "native_matrix", "needs: lint"),
("macos-release-check", "release_matrix", "needs: [lint, native-tests]"),
):
body = workflow.split(f"\n {job}:\n", 1)[1]
body = re.split(r"\n [a-z][a-z-]*:\n", body, maxsplit=1)[0]
self.assertIn("needs: lint", body)
self.assertIn(needs, body)
self.assertIn(f"fromJSON(needs.lint.outputs.{output})", body)
self.assertNotIn("pull_request.draft", body)
self.assertNotIn("matrix.tier", body)
@@ -183,15 +188,59 @@ class NativeSuiteTests(unittest.TestCase):
def test_native_caches_are_default_branch_snapshots_not_per_pr_uploads(self):
root = Path(__file__).resolve().parents[2]
workflow = (root / ".github/workflows/linux-lua.yml").read_text(encoding="utf-8")
for job in ("native-tests", "macos-release-check"):
body = workflow.split(f"\n {job}:\n", 1)[1]
body = re.split(r"\n [a-z][a-z-]*:\n", body, maxsplit=1)[0]
self.assertIn("revision: dependencies-v1", body)
self.assertIn("save-if: ${{ github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }}", body)
save = body.split("- name: Save compiled workload", 1)[1].split(" - name:", 1)[0]
self.assertIn("success() && steps.rust-cache.outputs.save-enabled == 'true'", save)
native = workflow.split("\n native-tests:\n", 1)[1].split("\n macos-release-check:\n", 1)[0]
for cache_id, save_name in (("rust-cache", "Save compiled workload"),
("macos-release-cache", "Save macOS release workload")):
cache = native.split(f"id: {cache_id}\n", 1)[1].split("\n - ", 1)[0]
self.assertIn("revision: dependencies-v1", cache)
self.assertIn("save-if: ${{ github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }}", cache)
save = native.split(f"- name: {save_name}", 1)[1].split("\n - ", 1)[0]
self.assertIn(f"success() && steps.{cache_id}.outputs.save-enabled == 'true'", save)
self.assertNotIn("always()", save)
self.assertIn("steps.rust-cache.outputs.cache-hit != 'true'", save)
self.assertIn(f"steps.{cache_id}.outputs.cache-hit != 'true'", save)
self.assertLess(native.index("Test complete workspace"), native.index("Save compiled workload"))
self.assertLess(native.index("Restore macOS release workload"), native.index("Test complete workspace"))
self.assertLess(native.index("Save compiled workload"), native.index("Check macOS release workspace"))
release_cache = native.split("- name: Restore macOS release workload", 1)[1].split("\n - ", 1)[0]
self.assertIn("key: native-release-check", release_cache)
self.assertIn("target: target/release", release_cache)
self.assertIn('restore-downloads: "false"', release_cache)
def test_macos_release_compilation_reuses_the_native_runner(self):
root = Path(__file__).resolve().parents[2]
workflow = (root / ".github/workflows/linux-lua.yml").read_text(encoding="utf-8")
native = workflow.split("\n native-tests:\n", 1)[1].split("\n macos-release-check:\n", 1)[0]
release = native.split("- name: Check macOS release workspace", 1)[1].split("\n - ", 1)[0]
self.assertIn("if: ${{ !cancelled() && runner.os == 'macOS' }}", release)
self.assertIn("run: cargo check --locked --workspace --release --timings", release)
self.assertNotIn("continue-on-error", native)
self.assertLess(native.index("Require the AppKit SDK"), native.index("Check macOS release workspace"))
self.assertLess(native.index("Restore macOS release workload"), native.index("Check macOS release workspace"))
self.assertLess(native.index("Check macOS release workspace"), native.index("Save macOS release workload"))
def test_release_required_contexts_reject_failed_or_missing_native_results(self):
root = Path(__file__).resolve().parents[2]
workflow = (root / ".github/workflows/linux-lua.yml").read_text(encoding="utf-8")
report = workflow.split("\n macos-release-check:\n", 1)[1]
self.assertIn("needs: [lint, native-tests]", report)
self.assertIn("if: ${{ !cancelled() && inputs.windows_diagnostics_run == '' }}", report)
self.assertIn("name: Release workspace (${{ matrix.os }})", report)
self.assertIn("fromJSON(needs.lint.outputs.release_matrix)", report)
self.assertIn("runs-on: ubuntu-24.04", report)
self.assertIn("shell: python", report)
self.assertIn("NATIVE_RESULT: ${{ needs.native-tests.result }}", report)
self.assertNotIn("uses:", report)
self.assertNotIn("continue-on-error", report)
script = textwrap.dedent(report.split(" run: |\n", 1)[1])
for result in ("success", "failure", "cancelled", "skipped", "", "success\n"):
with self.subTest(result=result):
completed = subprocess.run(
[sys.executable, "-c", script],
env={**os.environ, "NATIVE_RESULT": result},
capture_output=True, text=True, encoding="utf-8", check=False,
)
self.assertEqual(completed.returncode, 0 if result == "success" else 1,
completed.stderr)
def test_fast_test_profile_preserves_runtime_checks_and_resets_named_overrides(self):
root = Path(__file__).resolve().parents[2]