chore: import upstream snapshot with attribution
CI / typecheck · build · test · bundle-size (push) Failing after 1s
CI / clean-room dependency closure smoke (push) Failing after 1s
CI / server-runtime e2e (docker · pg + valkey) (push) Failing after 2s
Deploy Install Scripts / deploy (push) Failing after 2s
Windows / build (push) Has been cancelled
CI / typecheck · build · test · bundle-size (push) Failing after 1s
CI / clean-room dependency closure smoke (push) Failing after 1s
CI / server-runtime e2e (docker · pg + valkey) (push) Failing after 2s
Deploy Install Scripts / deploy (push) Failing after 2s
Windows / build (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,116 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: typecheck · build · test · bundle-size
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 25
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
|
||||
- name: Install Bun (worker runtime + test runner)
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: latest
|
||||
|
||||
# The repo intentionally gitignores package-lock.json (.gitignore), so
|
||||
# `cache: 'npm'` and `npm ci` (both require a committed lockfile) cannot
|
||||
# be used here — matches windows.yml / npm-publish.yml, which install the
|
||||
# same way.
|
||||
- name: Install dependencies
|
||||
run: npm install --no-audit --no-fund
|
||||
|
||||
- name: Typecheck
|
||||
run: npm run typecheck
|
||||
|
||||
# `npm run build` runs scripts/build-hooks.js, which enforces the worker
|
||||
# bundle-size guardrail (WORKER_SERVICE_MAX_BYTES, see #2584) and the MCP
|
||||
# server budget. A bundle that grows past threshold fails here → fails CI.
|
||||
- name: Build (includes bundle-size guardrails)
|
||||
run: npm run build
|
||||
|
||||
# The in-process server-runtime smoke test (tests/server/server-runtime-smoke.test.ts,
|
||||
# #2550) runs here with no Docker: it boots the server HTTP surface in
|
||||
# process, loads a mode, creates a key, makes an authed request, and
|
||||
# checks the viewer responds. This gives every PR real server-runtime
|
||||
# coverage. The full pg+redis e2e is the docker-gated job below.
|
||||
- name: Test
|
||||
run: bun test
|
||||
|
||||
clean-room-deps:
|
||||
name: clean-room dependency closure smoke
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 25
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
|
||||
- name: Install Bun (worker runtime + test runner)
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: latest
|
||||
|
||||
# See note in the build job: no committed root lockfile, so npm install.
|
||||
- name: Install dependencies
|
||||
run: npm install --no-audit --no-fund
|
||||
|
||||
# Run the frozen-lockfile drift check against the COMMITTED tree BEFORE
|
||||
# `npm run build` regenerates plugin/package.json + plugin/bun.lock (via
|
||||
# gen-plugin-lockfile.cjs). If a contributor changed plugin deps (through
|
||||
# scripts/build-hooks.js) but committed a stale plugin/bun.lock, the
|
||||
# committed pair is out of sync and --frozen-lockfile fails here.
|
||||
- name: Verify plugin lockfile is in sync (frozen-lockfile drift check)
|
||||
working-directory: plugin
|
||||
run: bun install --frozen-lockfile --ignore-scripts
|
||||
|
||||
- name: Build
|
||||
run: npm run build
|
||||
|
||||
# Clean-room install + import smoke test (plan-10): installs the packed
|
||||
# tarball into a throwaway dir and verifies the dependency closure resolves
|
||||
# and imports outside the dev tree.
|
||||
- name: Clean-room dependency closure smoke
|
||||
run: npm run smoke:clean-room
|
||||
|
||||
server-runtime-e2e-docker:
|
||||
name: server-runtime e2e (docker · pg + valkey)
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
# Docker is available on ubuntu-latest GitHub runners. This job runs the
|
||||
# full server-runtime e2e (#2550): real Postgres + Valkey, queue durability,
|
||||
# restart recovery, and revoked-key denial. It does not gate PRs from the
|
||||
# `build` job; a failure here surfaces a server-runtime regression before a
|
||||
# user can file one (plan-07 test matrix).
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
|
||||
- name: Install Bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: latest
|
||||
|
||||
# See note in the build job: no committed lockfile, so npm install.
|
||||
- name: Install dependencies
|
||||
run: npm install --no-audit --no-fund
|
||||
|
||||
- name: Verify Docker is available
|
||||
run: docker compose version
|
||||
|
||||
- name: Server-runtime Docker e2e
|
||||
run: npm run e2e:server:docker
|
||||
@@ -0,0 +1,41 @@
|
||||
name: Claude Code
|
||||
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
pull_request_review_comment:
|
||||
types: [created]
|
||||
issues:
|
||||
types: [opened, assigned]
|
||||
pull_request_review:
|
||||
types: [submitted]
|
||||
|
||||
jobs:
|
||||
claude:
|
||||
if: |
|
||||
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
|
||||
(github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
|
||||
(github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) ||
|
||||
(github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')))
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: read
|
||||
issues: read
|
||||
id-token: write
|
||||
actions: read
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Run Claude Code
|
||||
id: claude
|
||||
uses: anthropics/claude-code-action@v1
|
||||
with:
|
||||
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
|
||||
|
||||
additional_permissions: |
|
||||
actions: read
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
name: Close Tracked Issues
|
||||
|
||||
# When a "tracking" issue is closed, close every issue listed under its
|
||||
# "### Issues" section. Tracking issues consolidate several duplicate bug
|
||||
# reports that share one root cause; closing the tracker resolves them all.
|
||||
# PRs listed under "### PRs" are intentionally left untouched — those are
|
||||
# fixes to be merged, not closed.
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [closed]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
issue_number:
|
||||
description: 'Tracking issue number to fan-out close from'
|
||||
required: true
|
||||
type: number
|
||||
|
||||
jobs:
|
||||
close-tracked:
|
||||
runs-on: ubuntu-latest
|
||||
if: |
|
||||
(github.event_name == 'issues' && contains(github.event.issue.labels.*.name, 'tracking')) ||
|
||||
github.event_name == 'workflow_dispatch'
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
contents: read
|
||||
|
||||
steps:
|
||||
- name: Close issues tracked by this tracker
|
||||
uses: actions/github-script@v8
|
||||
with:
|
||||
script: |
|
||||
// Resolve the tracking issue (dispatch passes a number; event gives the payload).
|
||||
let tracker;
|
||||
if (context.eventName === 'workflow_dispatch') {
|
||||
const { data } = await github.rest.issues.get({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.payload.inputs.issue_number,
|
||||
});
|
||||
tracker = data;
|
||||
} else {
|
||||
tracker = context.payload.issue;
|
||||
}
|
||||
|
||||
// The `issues: closed` trigger guarantees a closed tracker. The manual
|
||||
// workflow_dispatch path bypasses that, and its only valid use is to
|
||||
// re-run the fan-out on a tracker that is *already* closed (e.g. a prior
|
||||
// run failed, or issues were added to the list after it closed). Refuse
|
||||
// to close children while the tracker is still open — otherwise a stray
|
||||
// dispatch on any issue with an "### Issues" section would bulk-close it.
|
||||
if (context.eventName === 'workflow_dispatch' && tracker.state !== 'closed') {
|
||||
console.log(`Tracker #${tracker.number} is open — refusing to fan-out close. `
|
||||
+ `Close the tracker first, then re-dispatch.`);
|
||||
return;
|
||||
}
|
||||
|
||||
const body = tracker.body || '';
|
||||
console.log(`Tracker #${tracker.number}: ${tracker.title}`);
|
||||
|
||||
// Isolate the "### Issues" section so we don't pick up PR numbers
|
||||
// from the "### PRs" block. The section runs until the next heading of
|
||||
// any level (#{2,} covers H2-H6) or end of body.
|
||||
const match = body.match(/###\s+Issues\s*\n([\s\S]*?)(?=\n#{2,}\s|\n*$)/i);
|
||||
if (!match) {
|
||||
console.log('No "### Issues" section found; nothing to close.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Collect every #NNNN referenced in that block, de-duplicated.
|
||||
const numbers = [...new Set(
|
||||
[...match[1].matchAll(/#(\d+)/g)].map((m) => parseInt(m[1], 10))
|
||||
)].filter((n) => n !== tracker.number);
|
||||
|
||||
if (numbers.length === 0) {
|
||||
console.log('No issue references found in the Issues section.');
|
||||
return;
|
||||
}
|
||||
console.log(`Tracked issues: ${numbers.map((n) => `#${n}`).join(', ')}`);
|
||||
|
||||
for (const issue_number of numbers) {
|
||||
try {
|
||||
const { data: target } = await github.rest.issues.get({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number,
|
||||
});
|
||||
|
||||
// Skip pull requests (the REST issues API returns them too).
|
||||
if (target.pull_request) {
|
||||
console.log(`#${issue_number} is a PR — skipping.`);
|
||||
continue;
|
||||
}
|
||||
if (target.state === 'closed') {
|
||||
console.log(`#${issue_number} already closed — skipping.`);
|
||||
continue;
|
||||
}
|
||||
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number,
|
||||
body: `Resolved via tracking issue #${tracker.number}, which has been closed. `
|
||||
+ `This report shared a root cause with others consolidated there — `
|
||||
+ `closing as part of that group. See #${tracker.number} for the details.`,
|
||||
});
|
||||
|
||||
await github.rest.issues.update({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number,
|
||||
state: 'closed',
|
||||
state_reason: 'completed',
|
||||
});
|
||||
|
||||
console.log(`Closed #${issue_number}.`);
|
||||
} catch (error) {
|
||||
// One bad reference shouldn't abort the rest of the fan-out.
|
||||
console.log(`Failed to close #${issue_number}: ${error.message}`);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
name: Convert Feature Requests to Discussions
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [labeled]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
issue_number:
|
||||
description: 'Issue number to convert to discussion'
|
||||
required: true
|
||||
type: number
|
||||
|
||||
jobs:
|
||||
convert:
|
||||
runs-on: ubuntu-latest
|
||||
if: |
|
||||
(github.event_name == 'issues' && github.event.label.name == 'feature-request') ||
|
||||
github.event_name == 'workflow_dispatch'
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
discussions: write
|
||||
contents: read
|
||||
|
||||
steps:
|
||||
- name: Get issue details and create discussion
|
||||
id: discussion
|
||||
uses: actions/github-script@v8
|
||||
with:
|
||||
script: |
|
||||
// Get issue details
|
||||
let issue;
|
||||
if (context.eventName === 'workflow_dispatch') {
|
||||
const { data } = await github.rest.issues.get({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.payload.inputs.issue_number
|
||||
});
|
||||
issue = data;
|
||||
} else {
|
||||
issue = context.payload.issue;
|
||||
}
|
||||
|
||||
console.log(`Processing issue #${issue.number}: ${issue.title}`);
|
||||
|
||||
// Format the discussion body with a reference to the original issue
|
||||
const discussionBody = `> Originally posted as issue #${issue.number} by @${issue.user.login}\n> ${issue.html_url}\n\n${issue.body || 'No description provided.'}`;
|
||||
|
||||
const mutation = `
|
||||
mutation($repositoryId: ID!, $categoryId: ID!, $title: String!, $body: String!) {
|
||||
createDiscussion(input: {
|
||||
repositoryId: $repositoryId
|
||||
categoryId: $categoryId
|
||||
title: $title
|
||||
body: $body
|
||||
}) {
|
||||
discussion {
|
||||
url
|
||||
number
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const variables = {
|
||||
repositoryId: 'R_kgDOPng1Jw',
|
||||
categoryId: 'DIC_kwDOPng1J84Cw86z',
|
||||
title: issue.title,
|
||||
body: discussionBody
|
||||
};
|
||||
|
||||
try {
|
||||
const result = await github.graphql(mutation, variables);
|
||||
const discussionUrl = result.createDiscussion.discussion.url;
|
||||
const discussionNumber = result.createDiscussion.discussion.number;
|
||||
|
||||
core.setOutput('url', discussionUrl);
|
||||
core.setOutput('number', discussionNumber);
|
||||
core.setOutput('issue_number', issue.number);
|
||||
|
||||
console.log(`Created discussion #${discussionNumber}: ${discussionUrl}`);
|
||||
return { discussionUrl, discussionNumber, issueNumber: issue.number };
|
||||
} catch (error) {
|
||||
core.setFailed(`Failed to create discussion: ${error.message}`);
|
||||
throw error;
|
||||
}
|
||||
|
||||
- name: Comment on issue
|
||||
uses: actions/github-script@v8
|
||||
with:
|
||||
script: |
|
||||
const issueNumber = ${{ steps.discussion.outputs.issue_number }};
|
||||
const discussionUrl = '${{ steps.discussion.outputs.url }}';
|
||||
|
||||
const comment = `This feature request has been moved to [Discussions](${discussionUrl}) to keep bug reports separate from feature ideas.\n\nPlease continue the conversation there - we'd love to hear your thoughts!`;
|
||||
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issueNumber,
|
||||
body: comment
|
||||
});
|
||||
|
||||
console.log(`Added comment to issue #${issueNumber}`);
|
||||
|
||||
- name: Close and lock issue
|
||||
uses: actions/github-script@v8
|
||||
with:
|
||||
script: |
|
||||
const issueNumber = ${{ steps.discussion.outputs.issue_number }};
|
||||
|
||||
// Close the issue
|
||||
await github.rest.issues.update({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issueNumber,
|
||||
state: 'closed'
|
||||
});
|
||||
|
||||
console.log(`Closed issue #${issueNumber}`);
|
||||
|
||||
// Lock the issue
|
||||
await github.rest.issues.lock({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issueNumber,
|
||||
lock_reason: 'resolved'
|
||||
});
|
||||
|
||||
console.log(`Locked issue #${issueNumber}`);
|
||||
@@ -0,0 +1,29 @@
|
||||
name: Deploy Install Scripts
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- 'openclaw/install.sh'
|
||||
- 'install/**'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Copy install scripts to deploy directory
|
||||
run: |
|
||||
mkdir -p install/public
|
||||
cp openclaw/install.sh install/public/openclaw.sh
|
||||
|
||||
- name: Deploy to Vercel
|
||||
uses: amondnet/vercel-action@v25
|
||||
with:
|
||||
vercel-token: ${{ secrets.VERCEL_TOKEN }}
|
||||
vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
|
||||
vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
|
||||
vercel-args: '--prod'
|
||||
working-directory: ./install
|
||||
@@ -0,0 +1,26 @@
|
||||
name: Publish to npm
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20'
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
- name: Install Bun (required by smoke:clean-room)
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: latest
|
||||
- run: npm install --ignore-scripts
|
||||
- run: npm run build
|
||||
- run: npm run smoke:clean-room
|
||||
- run: npm publish
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
@@ -0,0 +1,34 @@
|
||||
name: Summarize new issues
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [opened]
|
||||
|
||||
jobs:
|
||||
summary:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
models: read
|
||||
contents: read
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Run AI inference
|
||||
id: inference
|
||||
uses: actions/ai-inference@v2
|
||||
with:
|
||||
prompt: |
|
||||
Summarize the following GitHub issue in one paragraph:
|
||||
Title: ${{ github.event.issue.title }}
|
||||
Body: ${{ github.event.issue.body }}
|
||||
|
||||
- name: Comment with AI summary
|
||||
run: |
|
||||
gh issue comment "$ISSUE_NUMBER" --body "$RESPONSE"
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
ISSUE_NUMBER: ${{ github.event.issue.number }}
|
||||
RESPONSE: ${{ steps.inference.outputs.response }}
|
||||
@@ -0,0 +1,38 @@
|
||||
name: Windows
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'src/**'
|
||||
- 'plugin/scripts/**'
|
||||
- 'package.json'
|
||||
- 'bunfig.toml'
|
||||
- '.github/workflows/windows.yml'
|
||||
push:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
# Pinned to windows-2022 (VS2022 / v17). The windows-latest image moved to
|
||||
# windows-2025, which ships Visual Studio 18 — npm's bundled node-gyp@11.5.0
|
||||
# can't detect it ("unknown version undefined") and native tree-sitter
|
||||
# rebuilds fail during `npm install`. Revisit when node-gyp gains VS18 support.
|
||||
runs-on: windows-2022
|
||||
timeout-minutes: 25
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '22'
|
||||
|
||||
- name: Install Bun (worker runtime)
|
||||
run: |
|
||||
irm bun.sh/install.ps1 | iex
|
||||
shell: pwsh
|
||||
|
||||
- run: npm install --no-audit --no-fund
|
||||
|
||||
# Build only — the build-and-sync script also runs marketplace sync + worker
|
||||
# restart from a hardcoded ~/.claude/plugins path that doesn't exist on CI.
|
||||
- run: npm run build
|
||||
Reference in New Issue
Block a user