From 45535ff433a442284fd62e13ec1d95db1ea4c189 Mon Sep 17 00:00:00 2001 From: Tak Hoffman <781889+Takhoffman@users.noreply.github.com> Date: Fri, 27 Mar 2026 07:49:27 -0500 Subject: [PATCH] dev: speed up local check loop --- .github/workflows/ci.yml | 117 +++++++++++------- AGENTS.md | 16 ++- docs/.generated/plugin-sdk-api-baseline.json | 4 +- docs/.generated/plugin-sdk-api-baseline.jsonl | 4 +- git-hooks/pre-commit | 10 +- package.json | 4 +- scripts/ci-write-manifest-outputs.mjs | 1 - scripts/test-planner/planner.mjs | 4 - test/git-hooks-pre-commit.test.ts | 48 +++++++ test/scripts/test-planner.test.ts | 3 +- 10 files changed, 148 insertions(+), 63 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 85877b195a3..bce2b5f4b46 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,7 +35,6 @@ jobs: has_changed_extensions: ${{ steps.manifest.outputs.has_changed_extensions }} changed_extensions_matrix: ${{ steps.manifest.outputs.changed_extensions_matrix }} run_build_artifacts: ${{ steps.manifest.outputs.run_build_artifacts }} - run_release_check: ${{ steps.manifest.outputs.run_release_check }} run_checks_fast: ${{ steps.manifest.outputs.run_checks_fast }} checks_fast_matrix: ${{ steps.manifest.outputs.checks_fast_matrix }} run_checks: ${{ steps.manifest.outputs.run_checks }} @@ -278,34 +277,6 @@ jobs: include-hidden-files: true retention-days: 1 - # Validate npm pack contents after build (only on push to main, not PRs). - release-check: - needs: [preflight, build-artifacts] - if: needs.preflight.outputs.run_release_check == 'true' - runs-on: blacksmith-16vcpu-ubuntu-2404 - timeout-minutes: 20 - steps: - - name: Checkout - uses: actions/checkout@v6 - with: - persist-credentials: false - submodules: false - - - name: Setup Node environment - uses: ./.github/actions/setup-node-env - with: - install-bun: "false" - use-sticky-disk: "false" - - - name: Download dist artifact - uses: actions/download-artifact@v8 - with: - name: dist-build - path: dist/ - - - name: Check release contents - run: pnpm release:check - checks-fast: name: ${{ matrix.check_name }} needs: [preflight] @@ -432,8 +403,6 @@ jobs: node openclaw.mjs --help node openclaw.mjs status --json --timeout 1 pnpm test:build:singleton - node scripts/stage-bundled-plugin-runtime-deps.mjs - node --import tsx scripts/release-check.ts ;; *) echo "Unsupported checks task: $TASK" >&2 @@ -518,6 +487,51 @@ jobs: continue-on-error: true run: pnpm run lint:plugins:no-extension-imports + - name: Run no-random-messaging guard + id: no_random_messaging + continue-on-error: true + run: pnpm run lint:tmp:no-random-messaging + + - name: Run channel-agnostic boundary guard + id: channel_agnostic_boundaries + continue-on-error: true + run: pnpm run lint:tmp:channel-agnostic-boundaries + + - name: Run no-raw-channel-fetch guard + id: no_raw_channel_fetch + continue-on-error: true + run: pnpm run lint:tmp:no-raw-channel-fetch + + - name: Run ingress owner guard + id: ingress_owner + continue-on-error: true + run: pnpm run lint:agent:ingress-owner + + - name: Run no-register-http-handler guard + id: no_register_http_handler + continue-on-error: true + run: pnpm run lint:plugins:no-register-http-handler + + - name: Run no-monolithic plugin-sdk entry import guard + id: no_monolithic_plugin_sdk_entry_imports + continue-on-error: true + run: pnpm run lint:plugins:no-monolithic-plugin-sdk-entry-imports + + - name: Run no-extension-src-imports guard + id: no_extension_src_imports + continue-on-error: true + run: pnpm run lint:plugins:no-extension-src-imports + + - name: Run no-extension-test-core-imports guard + id: no_extension_test_core_imports + continue-on-error: true + run: pnpm run lint:plugins:no-extension-test-core-imports + + - name: Run plugin-sdk subpaths exported guard + id: plugin_sdk_subpaths_exported + continue-on-error: true + run: pnpm run lint:plugins:plugin-sdk-subpaths-exported + - name: Run web search provider boundary guard id: web_search_provider_boundary continue-on-error: true @@ -533,6 +547,11 @@ jobs: continue-on-error: true run: pnpm run lint:extensions:no-plugin-sdk-internal + - name: Run extension relative-outside-package guard + id: extension_relative_outside_package_boundary + continue-on-error: true + run: pnpm run lint:extensions:no-relative-outside-package + - name: Enforce safe external URL opening policy id: no_raw_window_open continue-on-error: true @@ -543,16 +562,6 @@ jobs: continue-on-error: true run: pnpm test:gateway:watch-regression - - name: Check config docs drift statefile - id: config_docs_drift - continue-on-error: true - run: pnpm config:docs:check - - - name: Check plugin SDK API baseline drift - id: plugin_sdk_api_drift - continue-on-error: true - run: pnpm plugin-sdk:api:check - - name: Upload gateway watch regression artifacts if: always() uses: actions/upload-artifact@v7 @@ -565,24 +574,40 @@ jobs: if: always() env: PLUGIN_EXTENSION_BOUNDARY_OUTCOME: ${{ steps.plugin_extension_boundary.outcome }} + NO_RANDOM_MESSAGING_OUTCOME: ${{ steps.no_random_messaging.outcome }} + CHANNEL_AGNOSTIC_BOUNDARIES_OUTCOME: ${{ steps.channel_agnostic_boundaries.outcome }} + NO_RAW_CHANNEL_FETCH_OUTCOME: ${{ steps.no_raw_channel_fetch.outcome }} + INGRESS_OWNER_OUTCOME: ${{ steps.ingress_owner.outcome }} + NO_REGISTER_HTTP_HANDLER_OUTCOME: ${{ steps.no_register_http_handler.outcome }} + NO_MONOLITHIC_PLUGIN_SDK_ENTRY_IMPORTS_OUTCOME: ${{ steps.no_monolithic_plugin_sdk_entry_imports.outcome }} + NO_EXTENSION_SRC_IMPORTS_OUTCOME: ${{ steps.no_extension_src_imports.outcome }} + NO_EXTENSION_TEST_CORE_IMPORTS_OUTCOME: ${{ steps.no_extension_test_core_imports.outcome }} + PLUGIN_SDK_SUBPATHS_EXPORTED_OUTCOME: ${{ steps.plugin_sdk_subpaths_exported.outcome }} WEB_SEARCH_PROVIDER_BOUNDARY_OUTCOME: ${{ steps.web_search_provider_boundary.outcome }} EXTENSION_SRC_OUTSIDE_PLUGIN_SDK_BOUNDARY_OUTCOME: ${{ steps.extension_src_outside_plugin_sdk_boundary.outcome }} EXTENSION_PLUGIN_SDK_INTERNAL_BOUNDARY_OUTCOME: ${{ steps.extension_plugin_sdk_internal_boundary.outcome }} + EXTENSION_RELATIVE_OUTSIDE_PACKAGE_BOUNDARY_OUTCOME: ${{ steps.extension_relative_outside_package_boundary.outcome }} NO_RAW_WINDOW_OPEN_OUTCOME: ${{ steps.no_raw_window_open.outcome }} GATEWAY_WATCH_REGRESSION_OUTCOME: ${{ steps.gateway_watch_regression.outcome }} - CONFIG_DOCS_DRIFT_OUTCOME: ${{ steps.config_docs_drift.outcome }} - PLUGIN_SDK_API_DRIFT_OUTCOME: ${{ steps.plugin_sdk_api_drift.outcome }} run: | failures=0 for result in \ "plugin-extension-boundary|$PLUGIN_EXTENSION_BOUNDARY_OUTCOME" \ + "lint:tmp:no-random-messaging|$NO_RANDOM_MESSAGING_OUTCOME" \ + "lint:tmp:channel-agnostic-boundaries|$CHANNEL_AGNOSTIC_BOUNDARIES_OUTCOME" \ + "lint:tmp:no-raw-channel-fetch|$NO_RAW_CHANNEL_FETCH_OUTCOME" \ + "lint:agent:ingress-owner|$INGRESS_OWNER_OUTCOME" \ + "lint:plugins:no-register-http-handler|$NO_REGISTER_HTTP_HANDLER_OUTCOME" \ + "lint:plugins:no-monolithic-plugin-sdk-entry-imports|$NO_MONOLITHIC_PLUGIN_SDK_ENTRY_IMPORTS_OUTCOME" \ + "lint:plugins:no-extension-src-imports|$NO_EXTENSION_SRC_IMPORTS_OUTCOME" \ + "lint:plugins:no-extension-test-core-imports|$NO_EXTENSION_TEST_CORE_IMPORTS_OUTCOME" \ + "lint:plugins:plugin-sdk-subpaths-exported|$PLUGIN_SDK_SUBPATHS_EXPORTED_OUTCOME" \ "web-search-provider-boundary|$WEB_SEARCH_PROVIDER_BOUNDARY_OUTCOME" \ "extension-src-outside-plugin-sdk-boundary|$EXTENSION_SRC_OUTSIDE_PLUGIN_SDK_BOUNDARY_OUTCOME" \ "extension-plugin-sdk-internal-boundary|$EXTENSION_PLUGIN_SDK_INTERNAL_BOUNDARY_OUTCOME" \ + "extension-relative-outside-package-boundary|$EXTENSION_RELATIVE_OUTSIDE_PACKAGE_BOUNDARY_OUTCOME" \ "lint:ui:no-raw-window-open|$NO_RAW_WINDOW_OPEN_OUTCOME" \ - "gateway-watch-regression|$GATEWAY_WATCH_REGRESSION_OUTCOME" \ - "config-docs-drift|$CONFIG_DOCS_DRIFT_OUTCOME" \ - "plugin-sdk-api-drift|$PLUGIN_SDK_API_DRIFT_OUTCOME"; do + "gateway-watch-regression|$GATEWAY_WATCH_REGRESSION_OUTCOME"; do name="${result%%|*}" outcome="${result#*|}" if [ "$outcome" != "success" ]; then diff --git a/AGENTS.md b/AGENTS.md index 8c4ab91ae5c..c0d869c9454 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -61,6 +61,7 @@ - Install deps: `pnpm install` - If deps are missing (for example `node_modules` missing, `vitest not found`, or `command not found`), run the repo’s package-manager install command (prefer lockfile/README-defined PM), then rerun the exact requested command once. Apply this to test/build/lint/typecheck/dev commands; if retry still fails, report the command and first actionable error. - Pre-commit hooks: `prek install`. The hook runs the repo verification flow, including `pnpm check`. +- `FAST_COMMIT=1` skips the repo-wide `pnpm format` and `pnpm check` inside the pre-commit hook only. Use it when you intentionally want a faster commit path and are running equivalent targeted verification manually. It does not change CI and does not change what `pnpm check` itself does. - Also supported: `bun install` (keep `pnpm-lock.yaml` + Bun patching in sync when touching deps/patches). - Prefer Bun for TypeScript execution (scripts, dev, tests): `bun ` / `bunx `. - Run CLI in dev: `pnpm openclaw ...` (bun) or `pnpm dev`. @@ -71,6 +72,15 @@ - Lint/format: `pnpm check` - Format check: `pnpm format` (oxfmt --check) - Format fix: `pnpm format:fix` (oxfmt --write) +- Terminology: + - "gate" means a verification command or command set that must be green for the decision you are making. + - A local dev gate is the fast default loop, usually `pnpm check` plus any scoped test you actually need. + - A landing gate is the broader bar before pushing `main`, usually `pnpm check`, `pnpm test`, and `pnpm build` when the touched surface can affect build output, packaging, lazy-loading/module boundaries, or published surfaces. + - A CI gate is whatever the relevant workflow enforces for that lane (for example `check`, `check-additional`, `build-smoke`, or release validation). +- Local dev gate: prefer `pnpm check` for the normal edit loop. It keeps the repo-architecture policy guards out of the default local loop. +- CI architecture gate: `check-additional` enforces architecture and boundary policy guards that are intentionally kept out of the default local loop. +- Formatting gate: the pre-commit hook runs `pnpm format` before `pnpm check`. If you want a formatting-only preflight locally, run `pnpm format` explicitly. +- If you need a fast commit loop, `FAST_COMMIT=1 git commit ...` skips the hook’s repo-wide `pnpm format` and `pnpm check`; use that only when you are deliberately covering the touched surface some other way. - Tests: `pnpm test` (vitest); coverage: `pnpm test:coverage` - Generated baseline artifacts live together under `docs/.generated/`. - Config schema drift uses `pnpm config:docs:gen` / `pnpm config:docs:check`. @@ -79,11 +89,11 @@ - For narrowly scoped changes, prefer narrowly scoped tests that directly validate the touched behavior. If no meaningful scoped test exists, say so explicitly and use the next most direct validation available. - Verification modes for work on `main`: - Default mode: `main` is relatively stable. Count pre-commit hook coverage when it already verified the current tree, avoid rerunning the exact same checks just for ceremony, and prefer keeping CI/main green before landing. - - YOLO mode: `main` is moving fast and the repo is effectively in a party-now-cleanup-after phase. Prefer explicit local verification close to the final landing point, and it is acceptable to use `--no-verify` for intermediate or catch-up commits after equivalent checks have already run locally. -- Preferred landing bar for pushes to `main`: in Default mode, favor `pnpm check` and `pnpm test` near the final rebase/push point when feasible. In YOLO mode, verify the touched surface locally near landing without insisting every intermediate commit replay the full hook. + - Fast-commit mode: `main` is moving fast and you intentionally optimize for shorter commit loops. Prefer explicit local verification close to the final landing point, and it is acceptable to use `--no-verify` for intermediate or catch-up commits after equivalent checks have already run locally. +- Preferred landing bar for pushes to `main`: in Default mode, favor `pnpm check` and `pnpm test` near the final rebase/push point when feasible. In fast-commit mode, verify the touched surface locally near landing without insisting every intermediate commit replay the full hook. - Scoped tests prove the change itself. `pnpm test` remains the default `main` landing bar; scoped tests do not replace full-suite gates by default. - Hard gate: if the change can affect build output, packaging, lazy-loading/module boundaries, or published surfaces, `pnpm build` MUST be run and MUST pass before pushing `main`. -- Default rule: do not land changes with failing format, lint, type, build, or required test checks when those failures are caused by the change or plausibly related to the touched surface. YOLO mode changes how verification is sequenced; it does not lower the requirement to validate and clean up the touched surface before final landing. +- Default rule: do not land changes with failing format, lint, type, build, or required test checks when those failures are caused by the change or plausibly related to the touched surface. Fast-commit mode changes how verification is sequenced; it does not lower the requirement to validate and clean up the touched surface before final landing. - For narrowly scoped changes, if unrelated failures already exist on latest `origin/main`, state that clearly, report the scoped tests you ran, and ask before broadening scope into unrelated fixes or landing despite those failures. - Do not use scoped tests as permission to ignore plausibly related failures. diff --git a/docs/.generated/plugin-sdk-api-baseline.json b/docs/.generated/plugin-sdk-api-baseline.json index b254fa12731..8fc65896a73 100644 --- a/docs/.generated/plugin-sdk-api-baseline.json +++ b/docs/.generated/plugin-sdk-api-baseline.json @@ -1395,7 +1395,7 @@ } }, { - "declaration": "export type ReplyPrefixContext = import(\"../auto-reply/reply/response-prefix-template.ts\").ResponsePrefixContext;", + "declaration": "export type ReplyPrefixContext = import(\"src/auto-reply/reply/response-prefix-template\").ResponsePrefixContext;", "exportName": "ReplyPrefixContext", "kind": "type", "source": { @@ -3500,7 +3500,7 @@ } }, { - "declaration": "export type ChannelOutboundSessionRouteParams = { cfg: OpenClawConfig; agentId: string; accountId?: string | null; target: string; resolvedTarget?: { to: string; kind: import(\"../channels/plugins/types.core.js\").ChannelDirectoryEntryKind | \"channel\"; display?: string; source: \"normalized\" | \"directory\"; }; replyToId?: string | null; threadId?: string | number | null;};", + "declaration": "export type ChannelOutboundSessionRouteParams = { cfg: OpenClawConfig; agentId: string; accountId?: string | null; target: string; resolvedTarget?: { to: string; kind: import(\"src/channels/plugins/types.core\").ChannelDirectoryEntryKind | \"channel\"; display?: string; source: \"normalized\" | \"directory\"; }; replyToId?: string | null; threadId?: string | number | null;};", "exportName": "ChannelOutboundSessionRouteParams", "kind": "type", "source": { diff --git a/docs/.generated/plugin-sdk-api-baseline.jsonl b/docs/.generated/plugin-sdk-api-baseline.jsonl index e7dbf6b6e36..2ea77a60d0e 100644 --- a/docs/.generated/plugin-sdk-api-baseline.jsonl +++ b/docs/.generated/plugin-sdk-api-baseline.jsonl @@ -152,7 +152,7 @@ {"declaration":"export function createChannelReplyPipeline(params: { cfg: OpenClawConfig; agentId: string; channel?: string | undefined; accountId?: string | undefined; typing?: CreateTypingCallbacksParams | undefined; typingCallbacks?: TypingCallbacks | undefined; }): ChannelReplyPipeline;","entrypoint":"channel-reply-pipeline","exportName":"createChannelReplyPipeline","importSpecifier":"openclaw/plugin-sdk/channel-reply-pipeline","kind":"function","recordType":"export","sourceLine":20,"sourcePath":"src/plugin-sdk/channel-reply-pipeline.ts"} {"declaration":"export type ChannelReplyPipeline = ChannelReplyPipeline;","entrypoint":"channel-reply-pipeline","exportName":"ChannelReplyPipeline","importSpecifier":"openclaw/plugin-sdk/channel-reply-pipeline","kind":"type","recordType":"export","sourceLine":16,"sourcePath":"src/plugin-sdk/channel-reply-pipeline.ts"} {"declaration":"export type CreateTypingCallbacksParams = CreateTypingCallbacksParams;","entrypoint":"channel-reply-pipeline","exportName":"CreateTypingCallbacksParams","importSpecifier":"openclaw/plugin-sdk/channel-reply-pipeline","kind":"type","recordType":"export","sourceLine":11,"sourcePath":"src/channels/typing.ts"} -{"declaration":"export type ReplyPrefixContext = import(\"../auto-reply/reply/response-prefix-template.ts\").ResponsePrefixContext;","entrypoint":"channel-reply-pipeline","exportName":"ReplyPrefixContext","importSpecifier":"openclaw/plugin-sdk/channel-reply-pipeline","kind":"type","recordType":"export","sourceLine":12,"sourcePath":"src/plugin-sdk/channel-reply-pipeline.ts"} +{"declaration":"export type ReplyPrefixContext = import(\"src/auto-reply/reply/response-prefix-template\").ResponsePrefixContext;","entrypoint":"channel-reply-pipeline","exportName":"ReplyPrefixContext","importSpecifier":"openclaw/plugin-sdk/channel-reply-pipeline","kind":"type","recordType":"export","sourceLine":12,"sourcePath":"src/plugin-sdk/channel-reply-pipeline.ts"} {"declaration":"export type ReplyPrefixContextBundle = ReplyPrefixContextBundle;","entrypoint":"channel-reply-pipeline","exportName":"ReplyPrefixContextBundle","importSpecifier":"openclaw/plugin-sdk/channel-reply-pipeline","kind":"type","recordType":"export","sourceLine":12,"sourcePath":"src/channels/reply-prefix.ts"} {"declaration":"export type ReplyPrefixOptions = ReplyPrefixOptions;","entrypoint":"channel-reply-pipeline","exportName":"ReplyPrefixOptions","importSpecifier":"openclaw/plugin-sdk/channel-reply-pipeline","kind":"type","recordType":"export","sourceLine":20,"sourcePath":"src/channels/reply-prefix.ts"} {"declaration":"export type TypingCallbacks = TypingCallbacks;","entrypoint":"channel-reply-pipeline","exportName":"TypingCallbacks","importSpecifier":"openclaw/plugin-sdk/channel-reply-pipeline","kind":"type","recordType":"export","sourceLine":4,"sourcePath":"src/channels/typing.ts"} @@ -385,7 +385,7 @@ {"declaration":"export type ChannelMessageActionContext = ChannelMessageActionContext;","entrypoint":"core","exportName":"ChannelMessageActionContext","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":482,"sourcePath":"src/channels/plugins/types.core.ts"} {"declaration":"export type ChannelMessagingAdapter = ChannelMessagingAdapter;","entrypoint":"core","exportName":"ChannelMessagingAdapter","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":395,"sourcePath":"src/channels/plugins/types.core.ts"} {"declaration":"export type ChannelOutboundSessionRoute = ChannelOutboundSessionRoute;","entrypoint":"core","exportName":"ChannelOutboundSessionRoute","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":309,"sourcePath":"src/channels/plugins/types.core.ts"} -{"declaration":"export type ChannelOutboundSessionRouteParams = { cfg: OpenClawConfig; agentId: string; accountId?: string | null; target: string; resolvedTarget?: { to: string; kind: import(\"../channels/plugins/types.core.js\").ChannelDirectoryEntryKind | \"channel\"; display?: string; source: \"normalized\" | \"directory\"; }; replyToId?: string | null; threadId?: string | number | null;};","entrypoint":"core","exportName":"ChannelOutboundSessionRouteParams","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":138,"sourcePath":"src/plugin-sdk/core.ts"} +{"declaration":"export type ChannelOutboundSessionRouteParams = { cfg: OpenClawConfig; agentId: string; accountId?: string | null; target: string; resolvedTarget?: { to: string; kind: import(\"src/channels/plugins/types.core\").ChannelDirectoryEntryKind | \"channel\"; display?: string; source: \"normalized\" | \"directory\"; }; replyToId?: string | null; threadId?: string | number | null;};","entrypoint":"core","exportName":"ChannelOutboundSessionRouteParams","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":138,"sourcePath":"src/plugin-sdk/core.ts"} {"declaration":"export type ChannelPlugin = ChannelPlugin;","entrypoint":"core","exportName":"ChannelPlugin","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":76,"sourcePath":"src/channels/plugins/types.plugin.ts"} {"declaration":"export type GatewayBindUrlResult = GatewayBindUrlResult;","entrypoint":"core","exportName":"GatewayBindUrlResult","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":1,"sourcePath":"src/shared/gateway-bind-url.ts"} {"declaration":"export type GatewayRequestHandlerOptions = GatewayRequestHandlerOptions;","entrypoint":"core","exportName":"GatewayRequestHandlerOptions","importSpecifier":"openclaw/plugin-sdk/core","kind":"type","recordType":"export","sourceLine":112,"sourcePath":"src/gateway/server-methods/types.ts"} diff --git a/git-hooks/pre-commit b/git-hooks/pre-commit index 11079bc9f22..51c3421d2af 100755 --- a/git-hooks/pre-commit +++ b/git-hooks/pre-commit @@ -53,5 +53,13 @@ git add -- "${files[@]}" # exist. Only run the repo-wide gate inside a real checkout. if [[ -f "$ROOT_DIR/package.json" ]] && [[ -f "$ROOT_DIR/pnpm-lock.yaml" ]]; then cd "$ROOT_DIR" - pnpm check + case "${FAST_COMMIT:-}" in + 1|true|TRUE|yes|YES|on|ON) + echo "FAST_COMMIT enabled: skipping pnpm format and pnpm check in pre-commit hook." + ;; + *) + pnpm format + pnpm check + ;; + esac fi diff --git a/package.json b/package.json index d57d344df00..ff381327802 100644 --- a/package.json +++ b/package.json @@ -682,7 +682,7 @@ "canon:check:json": "node scripts/canon.mjs check --json", "canon:enforce": "node scripts/canon.mjs enforce --json", "canvas:a2ui:bundle": "bash scripts/bundle-a2ui.sh", - "check": "pnpm check:no-conflict-markers && pnpm check:host-env-policy:swift && pnpm check:bundled-channel-config-metadata && pnpm check:base-config-schema && pnpm check:bundled-plugin-metadata && pnpm check:bundled-provider-auth-env-vars && pnpm format:check && pnpm tsgo && pnpm plugin-sdk:check-exports && pnpm lint && pnpm lint:tmp:no-random-messaging && pnpm lint:tmp:channel-agnostic-boundaries && pnpm lint:tmp:no-raw-channel-fetch && pnpm lint:agent:ingress-owner && pnpm lint:plugins:no-register-http-handler && pnpm lint:plugins:no-monolithic-plugin-sdk-entry-imports && pnpm lint:plugins:no-extension-src-imports && pnpm lint:plugins:no-extension-test-core-imports && pnpm lint:plugins:no-extension-imports && pnpm lint:plugins:plugin-sdk-subpaths-exported && pnpm lint:extensions:no-src-outside-plugin-sdk && pnpm lint:extensions:no-plugin-sdk-internal && pnpm lint:extensions:no-relative-outside-package && pnpm lint:web-search-provider-boundaries && pnpm lint:webhook:no-low-level-body-read && pnpm lint:auth:no-pairing-store-group && pnpm lint:auth:pairing-account-scope", + "check": "pnpm check:no-conflict-markers && pnpm check:host-env-policy:swift && pnpm tsgo && pnpm lint && pnpm lint:webhook:no-low-level-body-read && pnpm lint:auth:no-pairing-store-group && pnpm lint:auth:pairing-account-scope", "check:base-config-schema": "node --import tsx scripts/generate-base-config-schema.ts --check", "check:bundled-channel-config-metadata": "node --import tsx scripts/generate-bundled-channel-config-metadata.ts --check", "check:bundled-plugin-metadata": "node scripts/generate-bundled-plugin-metadata.mjs --check", @@ -775,7 +775,7 @@ "protocol:check": "pnpm protocol:gen && pnpm protocol:gen:swift && git diff --exit-code -- dist/protocol.schema.json apps/macos/Sources/OpenClawProtocol/GatewayModels.swift apps/shared/OpenClawKit/Sources/OpenClawProtocol/GatewayModels.swift", "protocol:gen": "node --import tsx scripts/protocol-gen.ts", "protocol:gen:swift": "node --import tsx scripts/protocol-gen-swift.ts", - "release:check": "pnpm config:docs:check && pnpm plugin-sdk:check-exports && pnpm plugin-sdk:api:check && node scripts/stage-bundled-plugin-runtime-deps.mjs && pnpm ui:build && node --import tsx scripts/release-check.ts", + "release:check": "pnpm check:base-config-schema && pnpm check:bundled-channel-config-metadata && pnpm check:bundled-plugin-metadata && pnpm check:bundled-provider-auth-env-vars && pnpm config:docs:check && pnpm plugin-sdk:check-exports && pnpm plugin-sdk:api:check && node scripts/stage-bundled-plugin-runtime-deps.mjs && pnpm ui:build && node --import tsx scripts/release-check.ts", "release:openclaw:npm:check": "node --import tsx scripts/openclaw-npm-release-check.ts", "release:openclaw:npm:verify-published": "node --import tsx scripts/openclaw-npm-postpublish-verify.ts", "release:plugins:npm:check": "node --import tsx scripts/plugin-npm-release-check.ts", diff --git a/scripts/ci-write-manifest-outputs.mjs b/scripts/ci-write-manifest-outputs.mjs index 4bf41778dc4..16754211feb 100644 --- a/scripts/ci-write-manifest-outputs.mjs +++ b/scripts/ci-write-manifest-outputs.mjs @@ -47,7 +47,6 @@ if (workflow === "ci") { writeOutput("has_changed_extensions", String(manifest.scope.hasChangedExtensions)); writeOutput("changed_extensions_matrix", JSON.stringify(manifest.scope.changedExtensionsMatrix)); writeOutput("run_build_artifacts", String(manifest.jobs.buildArtifacts.enabled)); - writeOutput("run_release_check", String(manifest.jobs.releaseCheck.enabled)); writeOutput("run_checks_fast", String(manifest.jobs.checksFast.enabled)); writeOutput("checks_fast_matrix", JSON.stringify(manifest.jobs.checksFast.matrix)); writeOutput("run_checks", String(manifest.jobs.checks.enabled)); diff --git a/scripts/test-planner/planner.mjs b/scripts/test-planner/planner.mjs index f881c81ce23..e8f4ee944e6 100644 --- a/scripts/test-planner/planner.mjs +++ b/scripts/test-planner/planner.mjs @@ -1172,8 +1172,6 @@ export function buildCIExecutionManifest(scopeInput = {}, options = {}) { "node openclaw.mjs --help", "node openclaw.mjs status --json --timeout 1", "pnpm test:build:singleton", - "node scripts/stage-bundled-plugin-runtime-deps.mjs", - "node --import tsx scripts/release-check.ts", ].join("\n"), }, ] @@ -1238,7 +1236,6 @@ export function buildCIExecutionManifest(scopeInput = {}, options = {}) { const jobs = { buildArtifacts: { enabled: nodeEligible, needsDistArtifacts: false }, - releaseCheck: { enabled: isPush && !scope.docsOnly && nodeEligible }, checksFast: { enabled: checksFastInclude.length > 0, matrix: { include: checksFastInclude } }, checks: { enabled: checksInclude.length > 0, matrix: { include: checksInclude } }, extensionFast: { @@ -1287,7 +1284,6 @@ export function buildCIExecutionManifest(scopeInput = {}, options = {}) { ...(docsEligible ? ["check-docs"] : []), ...(skillsPythonEligible ? ["skills-python"] : []), ...(nodeEligible ? ["build-artifacts"] : []), - ...(isPush && !scope.docsOnly && nodeEligible ? ["release-check"] : []), ], }; } diff --git a/test/git-hooks-pre-commit.test.ts b/test/git-hooks-pre-commit.test.ts index 5f608e4b9a2..7576bfd0f6a 100644 --- a/test/git-hooks-pre-commit.test.ts +++ b/test/git-hooks-pre-commit.test.ts @@ -72,4 +72,52 @@ describe("git-hooks/pre-commit (integration)", () => { const staged = run(dir, "git", ["diff", "--cached", "--name-only"]).split("\n").filter(Boolean); expect(staged).toEqual(["--all"]); }); + + it("skips pnpm format and pnpm check when FAST_COMMIT is enabled", () => { + const dir = mkdtempSync(path.join(os.tmpdir(), "openclaw-pre-commit-yolo-")); + run(dir, "git", ["init", "-q", "--initial-branch=main"]); + + mkdirSync(path.join(dir, "git-hooks"), { recursive: true }); + mkdirSync(path.join(dir, "scripts", "pre-commit"), { recursive: true }); + symlinkSync( + path.join(process.cwd(), "git-hooks", "pre-commit"), + path.join(dir, "git-hooks", "pre-commit"), + ); + writeFileSync( + path.join(dir, "scripts", "pre-commit", "run-node-tool.sh"), + "#!/usr/bin/env bash\nexit 0\n", + { + encoding: "utf8", + mode: 0o755, + }, + ); + writeFileSync( + path.join(dir, "scripts", "pre-commit", "filter-staged-files.mjs"), + "process.exit(0);\n", + "utf8", + ); + writeFileSync(path.join(dir, "package.json"), "{\"name\":\"tmp\"}\n", "utf8"); + writeFileSync(path.join(dir, "pnpm-lock.yaml"), "lockfileVersion: '9.0'\n", "utf8"); + + const fakeBinDir = path.join(dir, "bin"); + mkdirSync(fakeBinDir, { recursive: true }); + writeExecutable(fakeBinDir, "node", "#!/usr/bin/env bash\nexit 0\n"); + writeExecutable( + fakeBinDir, + "pnpm", + "#!/usr/bin/env bash\necho 'pnpm should not run when FAST_COMMIT is enabled' >&2\nexit 99\n", + ); + + writeFileSync(path.join(dir, "tracked.txt"), "hello\n", "utf8"); + run(dir, "git", ["add", "--", "tracked.txt"]); + + const output = run(dir, "bash", ["git-hooks/pre-commit"], { + PATH: `${fakeBinDir}:${process.env.PATH ?? ""}`, + FAST_COMMIT: "1", + }); + + expect(output).toContain( + "FAST_COMMIT enabled: skipping pnpm format and pnpm check in pre-commit hook.", + ); + }); }); diff --git a/test/scripts/test-planner.test.ts b/test/scripts/test-planner.test.ts index aeab331af5b..b6d6d134293 100644 --- a/test/scripts/test-planner.test.ts +++ b/test/scripts/test-planner.test.ts @@ -356,7 +356,7 @@ describe("test planner", () => { expect(manifest.jobs.checkDocs.enabled).toBe(true); }); - it("adds push-only compat and release lanes to push manifests", () => { + it("adds the push-only compat lane to push manifests", () => { const manifest = buildCIExecutionManifest( { eventName: "push", @@ -374,7 +374,6 @@ describe("test planner", () => { }, ); - expect(manifest.jobs.releaseCheck.enabled).toBe(true); expect( manifest.jobs.checks.matrix.include.some((entry) => entry.task === "compat-node22"), ).toBe(true);