源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
Dependency audit / audit (push) Canceled after 0s
CI / plan (push) Canceled after 0s
Gitoxide helper admission / macos-latest (push) Canceled after 0s
Gitoxide helper admission / ubuntu-latest (push) Canceled after 0s
Gitoxide helper admission / windows-latest (push) Canceled after 0s
Runtime Host peer admission / quality (push) Canceled after 0s
Windows recovery / windows_recovery (push) Canceled after 0s
CI / heavy (push) Canceled after 0s
CI / test (push) Canceled after 0s
Dependency audit / audit (push) Canceled after 0s
CI / plan (push) Canceled after 0s
Gitoxide helper admission / macos-latest (push) Canceled after 0s
Gitoxide helper admission / ubuntu-latest (push) Canceled after 0s
Gitoxide helper admission / windows-latest (push) Canceled after 0s
Runtime Host peer admission / quality (push) Canceled after 0s
Windows recovery / windows_recovery (push) Canceled after 0s
CI / heavy (push) Canceled after 0s
CI / test (push) Canceled after 0s
This commit is contained in:
@@ -0,0 +1,411 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
name: CI
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [main]
|
||||
push:
|
||||
branches: [main]
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ci-${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
# Path planning and install-free repository contracts stay on the cheap lane
|
||||
# that every pull request must run. The outputs select the separate heavy lane
|
||||
# without teaching the workflow a second copy of the path rules.
|
||||
plan:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 45
|
||||
outputs:
|
||||
asf_source: ${{ steps.plan.outputs.asf_source }}
|
||||
astryx_surface: ${{ steps.plan.outputs.astryx_surface }}
|
||||
cli_package: ${{ steps.plan.outputs.cli_package }}
|
||||
code: ${{ steps.plan.outputs.code }}
|
||||
e2e: ${{ steps.plan.outputs.e2e }}
|
||||
heavy: ${{ steps.plan.outputs.heavy }}
|
||||
release_contract: ${{ steps.plan.outputs.release_contract }}
|
||||
runtime_host: ${{ steps.plan.outputs.runtime_host }}
|
||||
runtime_sandbox: ${{ steps.plan.outputs.runtime_sandbox }}
|
||||
storage_stress: ${{ steps.plan.outputs.storage_stress }}
|
||||
storybook: ${{ steps.plan.outputs.storybook }}
|
||||
standard_workspaces: ${{ steps.plan.outputs.standard_workspaces }}
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
|
||||
- id: plan
|
||||
name: Select affected test surfaces
|
||||
env:
|
||||
BASE_SHA: ${{ github.event_name == 'push' && github.event.before || github.event.pull_request.base.sha }}
|
||||
HEAD_SHA: ${{ github.event_name == 'push' && github.sha || github.event.pull_request.head.sha }}
|
||||
run: |
|
||||
# PR checks may predate later main changes, so the planning lane also
|
||||
# validates the exact merged delta. Dispatches and unavailable history
|
||||
# fail safe to every surface.
|
||||
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]] || [[ "$BASE_SHA" =~ ^0+$ ]] || ! git cat-file -e "${BASE_SHA}^{commit}"; then
|
||||
node scripts/ci-test-plan.mjs --full >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
node scripts/ci-test-plan.mjs --base "$BASE_SHA" --head "$HEAD_SHA" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
- name: Test CI planner
|
||||
run: node --test --test-concurrency=1 scripts/ci-test-plan.test.mjs scripts/verify-windows-harness.test.mjs
|
||||
|
||||
# Pure Node like the planner test, and the labelling workflow imports this
|
||||
# module directly, so a tier or exclusion change is caught here rather
|
||||
# than by mislabelling live pull requests.
|
||||
- name: Test PR effort classification
|
||||
run: node --test --test-concurrency=1 scripts/pr-effort.test.mjs
|
||||
|
||||
# The scheduled lifecycle workflow imports this pure policy module. Keep
|
||||
# its time boundaries and exemptions deterministic before it can write.
|
||||
- name: Test issue and PR lifecycle policy
|
||||
run: node --test --test-concurrency=1 scripts/issue-pr-lifecycle.test.mjs
|
||||
|
||||
# Same shape and the same needs: a regenerate-and-diff contract that runs
|
||||
# on Node alone, so it belongs beside the planner test rather than behind
|
||||
# an install.
|
||||
- name: Check Windows test inventory
|
||||
run: npm run windows:inventory
|
||||
|
||||
# Runs on the PR merge result: after a sibling protocol change lands on
|
||||
# main with the same epoch text, the silently merged tree still carries
|
||||
# the current base parent's epoch and this fails instead of shipping two
|
||||
# incompatible protocols under one number (#3313).
|
||||
- name: Guard the protocol compatibility epoch
|
||||
if: github.event_name == 'pull_request'
|
||||
run: node scripts/protocol-epoch-check.mjs --base 'HEAD^1'
|
||||
|
||||
- name: Test the epoch guard
|
||||
run: node --test --test-concurrency=1 scripts/protocol-epoch-check.test.mjs
|
||||
|
||||
- name: Test Computer Use script contracts
|
||||
run: node --test scripts/ax-tree-audit.test.mjs scripts/computer-use/lab-root.test.mjs
|
||||
|
||||
- name: Test script entrypoint contracts
|
||||
run: node --test scripts/script-entrypoints.test.mjs
|
||||
|
||||
# Install-free like its neighbours: the gate reads one source file and
|
||||
# compares it to a hand-edited inventory, so a hook that silently widens
|
||||
# its scope to the whole tree fails here rather than in a profile (#4109).
|
||||
- name: Check the hooks scoped to the whole shell
|
||||
run: npm run check:app-shell-hooks
|
||||
|
||||
- name: Test the app-shell hook gate
|
||||
run: node --test --test-concurrency=1 scripts/check-app-shell-hooks.test.mjs
|
||||
|
||||
- name: Verify ASF npm preflight policy
|
||||
run: npm run check:asf-npm
|
||||
|
||||
# The source-header gate has to see every file that lands, not only the
|
||||
# files an affected surface selects, so it runs unconditionally beside
|
||||
# the other install-free checks.
|
||||
- name: Check ASF source headers
|
||||
run: npm run check:asf-headers
|
||||
|
||||
heavy:
|
||||
needs: plan
|
||||
if: needs.plan.outputs.heavy == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 45
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
fetch-depth: 0
|
||||
persist-credentials: false
|
||||
|
||||
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
||||
with:
|
||||
node-version: '24'
|
||||
cache: npm
|
||||
|
||||
- name: Select the release npm toolchain
|
||||
if: needs.plan.outputs.cli_package == 'true'
|
||||
run: npm install --global --no-audit --no-fund "$(node -p 'require("./package.json").packageManager')"
|
||||
|
||||
- name: Restore Electron artifact cache
|
||||
if: needs.plan.outputs.code == 'true'
|
||||
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
||||
with:
|
||||
path: ~/.cache/electron
|
||||
key: electron-${{ runner.os }}-${{ hashFiles('package-lock.json') }}
|
||||
restore-keys: electron-${{ runner.os }}-
|
||||
|
||||
- name: Install Linux runtime dependencies
|
||||
if: needs.plan.outputs.runtime_sandbox == 'true'
|
||||
run: sudo apt-get update && sudo apt-get install -y ripgrep bubblewrap
|
||||
|
||||
# Ubuntu 24.04 hosted runners gate unprivileged user namespaces through
|
||||
# AppArmor, which otherwise makes bwrap fail while configuring loopback.
|
||||
- name: Enable bubblewrap user namespaces
|
||||
if: needs.plan.outputs.runtime_sandbox == 'true'
|
||||
run: |
|
||||
if [[ -e /proc/sys/kernel/apparmor_restrict_unprivileged_userns ]]; then
|
||||
sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0
|
||||
fi
|
||||
if [[ -e /proc/sys/kernel/unprivileged_userns_clone ]]; then
|
||||
sudo sysctl -w kernel.unprivileged_userns_clone=1
|
||||
fi
|
||||
|
||||
- name: Install dependencies
|
||||
if: needs.plan.outputs.code == 'true' || needs.plan.outputs.astryx_surface == 'true' || needs.plan.outputs.asf_source == 'true' || needs.plan.outputs.cli_package == 'true' || needs.plan.outputs.release_contract == 'true'
|
||||
run: npm ci
|
||||
|
||||
# The header audit above remains install-free. The complete source gate
|
||||
# also exercises generation and therefore runs after its pinned formatter
|
||||
# dependency is installed, matching the source-candidate workflow.
|
||||
- name: Verify ASF source release mechanics
|
||||
if: needs.plan.outputs.asf_source == 'true'
|
||||
run: npm run check:asf-source
|
||||
|
||||
# Parsed dependency rules and an exact legacy-debt ledger keep the
|
||||
# renderer root from absorbing new feature or Desktop ownership while
|
||||
# the existing AppShell is migrated behind stable boundaries.
|
||||
- name: Check renderer architecture
|
||||
if: needs.plan.outputs.code == 'true'
|
||||
env:
|
||||
BASE_SHA: ${{ github.event_name == 'push' && github.event.before || github.event.pull_request.base.sha }}
|
||||
run: |
|
||||
if [[ -n "$BASE_SHA" && ! "$BASE_SHA" =~ ^0+$ ]]; then
|
||||
npm run check:renderer-architecture -- --base "$BASE_SHA"
|
||||
else
|
||||
npm run check:renderer-architecture
|
||||
fi
|
||||
|
||||
- name: Lint
|
||||
if: needs.plan.outputs.code == 'true'
|
||||
run: npm run lint
|
||||
|
||||
- name: Check formatting
|
||||
if: needs.plan.outputs.code == 'true'
|
||||
run: npm run format:check
|
||||
|
||||
# This generated artifact describes the whole renderer/UI tree, so every
|
||||
# code-validation run checks it even when the triggering diff is outside
|
||||
# an Astryx surface. Keep it before Build so stale output is reported
|
||||
# directly instead of being hidden behind an earlier compilation failure.
|
||||
- name: Astryx surface inventory
|
||||
if: needs.plan.outputs.code == 'true' || needs.plan.outputs.astryx_surface == 'true'
|
||||
run: |
|
||||
npm run astryx:surface-inventory
|
||||
npm run astryx:surface-inventory:test
|
||||
|
||||
- name: Build
|
||||
if: needs.plan.outputs.code == 'true' || needs.plan.outputs.cli_package == 'true' || needs.plan.outputs.release_contract == 'true'
|
||||
run: npm run build
|
||||
|
||||
- name: Release contracts
|
||||
if: needs.plan.outputs.release_contract == 'true'
|
||||
run: npm run check:release
|
||||
|
||||
- name: Typecheck
|
||||
if: needs.plan.outputs.code == 'true'
|
||||
run: npm run typecheck
|
||||
|
||||
# Two drift contracts over the shipped app-icon artwork, sitting beside
|
||||
# the theme drift check for the same reason: the committed bytes are a
|
||||
# build output that nothing else re-derives, so without this a change to
|
||||
# the generator, to DEFAULT_APP_ICON, or to the packaging config can go
|
||||
# green while the artwork it names no longer matches.
|
||||
- name: App icon artwork drift
|
||||
if: needs.plan.outputs.code == 'true'
|
||||
run: node --test scripts/verify-packaged-app-icons.test.mjs scripts/generate-app-icons.test.mjs
|
||||
|
||||
- name: Astryx theme drift
|
||||
if: needs.plan.outputs.code == 'true'
|
||||
run: npm run astryx:theme -- --check
|
||||
|
||||
- name: Knip (apps/desktop)
|
||||
if: needs.plan.outputs.code == 'true'
|
||||
run: npx knip --workspace apps/desktop
|
||||
|
||||
- name: Knip (packages/ui)
|
||||
if: needs.plan.outputs.code == 'true'
|
||||
run: npx knip --workspace packages/ui
|
||||
|
||||
- name: Linux sandbox smoke
|
||||
if: needs.plan.outputs.runtime_sandbox == 'true'
|
||||
env:
|
||||
MAKA_REQUIRE_LINUX_SANDBOX_SMOKE: '1'
|
||||
run: npm exec -w @maka/runtime -- node --test dist/__tests__/linux-sandbox-smoke.test.js
|
||||
|
||||
- name: Run affected standard workspace tests
|
||||
if: needs.plan.outputs.standard_workspaces != ''
|
||||
env:
|
||||
STORAGE_STRESS: ${{ needs.plan.outputs.storage_stress }}
|
||||
WORKSPACES: ${{ needs.plan.outputs.standard_workspaces }}
|
||||
run: |
|
||||
if [[ "$STORAGE_STRESS" == "true" ]]; then
|
||||
export MAKA_STORAGE_STRESS=1
|
||||
fi
|
||||
node scripts/run-workspace-tests-parallel.mjs --concurrency=3 --workspaces="$WORKSPACES"
|
||||
|
||||
- name: Run live Eval egress proxy test
|
||||
if: contains(needs.plan.outputs.standard_workspaces, 'packages/eval')
|
||||
env:
|
||||
MAKA_EVAL_EGRESS_PROXY_TEST: '1'
|
||||
run: |
|
||||
docker pull python:3.12-slim
|
||||
docker build \
|
||||
--tag maka-eval-egress-proxy:12.2.3 \
|
||||
--file packages/eval/harbor/egress-proxy/Dockerfile \
|
||||
packages/eval/harbor
|
||||
npm --workspace @maka/eval run test:egress-proxy:live
|
||||
|
||||
- name: Run Runtime Host tests
|
||||
if: needs.plan.outputs.runtime_host == 'true'
|
||||
run: npm --workspace @maka/runtime-host run test:dist
|
||||
|
||||
- name: Ensure xvfb
|
||||
if: needs.plan.outputs.e2e == 'true'
|
||||
run: command -v xvfb-run >/dev/null 2>&1 || { sudo apt-get update && sudo apt-get install -y xvfb; }
|
||||
|
||||
- name: Desktop e2e
|
||||
if: needs.plan.outputs.e2e == 'true'
|
||||
run: xvfb-run -a npm exec -w @maka/desktop -- playwright test --config e2e/playwright.config.ts
|
||||
|
||||
- name: Browser WebContentsView semantic smoke
|
||||
if: needs.plan.outputs.e2e == 'true'
|
||||
# Hosted Linux runners cannot configure Electron's SUID helper. This
|
||||
# smoke loads only its loopback fixture; production stays sandboxed.
|
||||
run: xvfb-run -a npm exec --workspace @maka/desktop -- electron --no-sandbox scripts/browser-observe-act-smoke.mjs
|
||||
|
||||
- name: Alignment audit
|
||||
if: needs.plan.outputs.e2e == 'true'
|
||||
run: xvfb-run -a node scripts/audit-alignment.mjs
|
||||
|
||||
- name: Install Playwright Chromium
|
||||
if: needs.plan.outputs.storybook == 'true'
|
||||
run: npx playwright install --with-deps chromium
|
||||
|
||||
- name: Build Storybook
|
||||
if: needs.plan.outputs.storybook == 'true'
|
||||
run: npm --workspace @maka/desktop run build-storybook
|
||||
|
||||
- name: Storybook smoke
|
||||
if: needs.plan.outputs.storybook == 'true'
|
||||
run: npm --workspace @maka/desktop run smoke:storybook
|
||||
|
||||
- name: Update stable Rust for CLI packaging
|
||||
if: needs.plan.outputs.cli_package == 'true'
|
||||
run: rustup update stable --no-self-update
|
||||
|
||||
- id: cli-rustc
|
||||
name: Resolve CLI Rust cache version
|
||||
if: needs.plan.outputs.cli_package == 'true'
|
||||
shell: bash
|
||||
run: |
|
||||
echo "version=$(rustc --version | cut -d ' ' -f 2)" >> "$GITHUB_OUTPUT"
|
||||
echo "revision=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
|
||||
{
|
||||
echo "KACHE_CACHE_DIR=${{ runner.temp }}/kache-cli-package"
|
||||
echo "KACHE_RUNTIME_DIR=${{ runner.temp }}/kache-cli-package-runtime"
|
||||
echo "RUSTC_WRAPPER=kache"
|
||||
} >> "$GITHUB_ENV"
|
||||
|
||||
- name: Install Kache for CLI packaging
|
||||
if: needs.plan.outputs.cli_package == 'true'
|
||||
uses: taiki-e/install-action@1ed6d7be6168f6c9046541087ff549b6bc581fdf # v2
|
||||
with:
|
||||
tool: kache@0.16.0
|
||||
|
||||
- id: cli-kache-cache
|
||||
name: Restore CLI Rust build cache
|
||||
if: needs.plan.outputs.cli_package == 'true'
|
||||
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
||||
with:
|
||||
path: ${{ runner.temp }}/kache-cli-package
|
||||
key: kache-runtime-host-peer-cli-package-v0.16.0-${{ runner.os }}-${{ runner.arch }}-rust-${{ steps.cli-rustc.outputs.version }}-${{ steps.cli-rustc.outputs.revision }}
|
||||
restore-keys: |
|
||||
kache-runtime-host-peer-cli-package-v0.16.0-${{ runner.os }}-${{ runner.arch }}-rust-${{ steps.cli-rustc.outputs.version }}-
|
||||
|
||||
- name: Install cargo-deny for CLI packaging
|
||||
if: needs.plan.outputs.cli_package == 'true'
|
||||
uses: taiki-e/install-action@1ed6d7be6168f6c9046541087ff549b6bc581fdf # v2
|
||||
with:
|
||||
tool: cargo-deny@0.20.2
|
||||
|
||||
- name: Build CLI release candidate
|
||||
if: needs.plan.outputs.cli_package == 'true'
|
||||
run: npm run release:cli:pack -- --allow-dirty
|
||||
|
||||
- name: Report CLI Rust build cache
|
||||
if: needs.plan.outputs.cli_package == 'true'
|
||||
shell: bash
|
||||
run: kache report --format github >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
- name: Save CLI Rust build cache
|
||||
if: needs.plan.outputs.cli_package == 'true' && github.ref_name == github.event.repository.default_branch
|
||||
uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
||||
with:
|
||||
path: ${{ runner.temp }}/kache-cli-package
|
||||
key: ${{ steps.cli-kache-cache.outputs.cache-primary-key }}
|
||||
|
||||
- name: Validate installed CLI release candidate
|
||||
if: needs.plan.outputs.cli_package == 'true'
|
||||
run: npm run release:cli:smoke -- packages/cli/release/*.tgz
|
||||
|
||||
# Branch protection requires the `test` context on every pull request. Keep
|
||||
# this aggregation job unconditional so documentation-only plans report
|
||||
# success while selected heavy validation still propagates every failure.
|
||||
test:
|
||||
needs: [plan, heavy]
|
||||
if: always()
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 45
|
||||
steps:
|
||||
- name: Report required test status
|
||||
env:
|
||||
HEAVY_RESULT: ${{ needs.heavy.result }}
|
||||
HEAVY_SELECTED: ${{ needs.plan.outputs.heavy }}
|
||||
PLAN_RESULT: ${{ needs.plan.result }}
|
||||
run: |
|
||||
if [[ "$PLAN_RESULT" != "success" ]]; then
|
||||
echo "::error::CI planning and lightweight checks ended with $PLAN_RESULT"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
case "$HEAVY_SELECTED" in
|
||||
true)
|
||||
if [[ "$HEAVY_RESULT" != "success" ]]; then
|
||||
echo "::error::Selected heavy CI ended with $HEAVY_RESULT"
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
false)
|
||||
if [[ "$HEAVY_RESULT" != "skipped" ]]; then
|
||||
echo "::error::Unselected heavy CI ended with $HEAVY_RESULT instead of skipped"
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
echo "::error::CI planner produced an invalid heavy selection: $HEAVY_SELECTED"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
echo "Required CI passed (heavy selected: $HEAVY_SELECTED)"
|
||||
Reference in New Issue
Block a user