mirror of
https://github.com/tiennm99/ccs.git
synced 2026-07-16 10:16:49 +00:00
feat(ci): add workflow_dispatch for AI review (#291)
* feat(ci): add AI code review workflow with Claude via CLIProxyAPI - Self-hosted runner calls CLIProxyAPI at localhost:8317 - Triggers on PR open/update and /review comment - Uses gemini-claude-opus-4-5-thinking for deep reviews - Posts summary + inline comments via gh CLI - Handles self-PR fallback to COMMENT mode * chore(release): 7.15.0-dev.1 [skip ci] * refactor(ci): use GitHub App for reviewer identity + new review format - Posts as ccs-agy-reviewer[bot] via GitHub App token - New review format: structured markdown with verdict, summary, issues table - Single PR comment instead of inline comments - Concise, focused on PR changes only * chore(release): 7.15.0-dev.2 [skip ci] * refactor(ci): switch to Claude Code CLI for reviews - Use claude -p instead of custom TypeScript script - Auto-install if not present on runner - CLIProxyAPI via env vars (ANTHROPIC_BASE_URL, ANTHROPIC_MODEL) - Allowed tools: Read, Glob, Grep - Max 3 turns for file exploration * chore(release): 7.15.0-dev.3 [skip ci] * chore(release): 7.15.0-dev.4 [skip ci] * feat(ci): add workflow_dispatch for manual review trigger * chore(release): 7.15.0-dev.5 [skip ci] * refactor(cliproxy): add faulty version range infrastructure (#289) * refactor(cliproxy): add faulty version range infrastructure - Add CLIPROXY_FAULTY_RANGE constant for marking known buggy versions - Add isVersionFaulty() helper to version-checker.ts - Update lifecycle.ts to warn users on faulty versions with upgrade suggestion - Update health checks to distinguish faulty vs experimental versions - Update stats routes to include faultyRange in API response - Skip faulty versions when calculating latestStable Infrastructure ready for future version promotions. Currently: - v80 and below: stable - v81+: marked as faulty (context cancellation bugs) * chore: trigger review * chore: re-trigger review * chore: test PR trigger --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
This commit is contained in:
co-authored by
github-actions[bot] <github-actions[bot]@users.noreply.github.com>
parent
49c4d299c0
commit
b6d65209cd
@@ -13,10 +13,16 @@ on:
|
||||
types: [opened, synchronize, reopened]
|
||||
issue_comment:
|
||||
types: [created]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
pr_number:
|
||||
description: 'PR number to review'
|
||||
required: true
|
||||
type: string
|
||||
|
||||
# Cancel in-progress runs for same PR
|
||||
concurrency:
|
||||
group: ai-review-${{ github.event.pull_request.number || github.event.issue.number }}
|
||||
group: ai-review-${{ github.event.pull_request.number || github.event.issue.number || github.event.inputs.pr_number }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
@@ -30,6 +36,7 @@ jobs:
|
||||
# - Comment event: only if it's a PR and contains /review
|
||||
if: >
|
||||
github.event_name == 'pull_request' ||
|
||||
github.event_name == 'workflow_dispatch' ||
|
||||
(github.event_name == 'issue_comment' &&
|
||||
github.event.issue.pull_request &&
|
||||
contains(github.event.comment.body, '/review'))
|
||||
@@ -52,6 +59,8 @@ jobs:
|
||||
run: |
|
||||
if [ "${{ github.event_name }}" = "pull_request" ]; then
|
||||
echo "number=${{ github.event.pull_request.number }}" >> $GITHUB_OUTPUT
|
||||
elif [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
||||
echo "number=${{ github.event.inputs.pr_number }}" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "number=${{ github.event.issue.number }}" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@kaitranntt/ccs",
|
||||
"version": "7.15.0-dev.3",
|
||||
"version": "7.15.0-dev.5",
|
||||
"description": "Claude Code Switch - Instant profile switching between Claude Sonnet 4.5 and GLM 4.6",
|
||||
"keywords": [
|
||||
"cli",
|
||||
|
||||
@@ -5,12 +5,17 @@
|
||||
|
||||
import * as fs from 'fs';
|
||||
import { BinaryManagerConfig } from '../types';
|
||||
import { checkForUpdates, fetchLatestVersion, isNewerVersion } from './version-checker';
|
||||
import {
|
||||
checkForUpdates,
|
||||
fetchLatestVersion,
|
||||
isNewerVersion,
|
||||
isVersionFaulty,
|
||||
} from './version-checker';
|
||||
import { downloadAndInstall, deleteBinary, getBinaryPath } from './installer';
|
||||
import { info, warn } from '../../utils/ui';
|
||||
import { isCliproxyRunning } from '../stats-fetcher';
|
||||
import { CLIPROXY_DEFAULT_PORT } from '../config-generator';
|
||||
import { CLIPROXY_MAX_STABLE_VERSION } from '../platform-detector';
|
||||
import { CLIPROXY_MAX_STABLE_VERSION, CLIPROXY_FAULTY_RANGE } from '../platform-detector';
|
||||
|
||||
/** Log helper */
|
||||
function log(message: string, verbose: boolean): void {
|
||||
@@ -46,15 +51,26 @@ async function handleAutoUpdate(config: BinaryManagerConfig, verbose: boolean):
|
||||
const currentVersion = updateResult.currentVersion;
|
||||
const latestVersion = updateResult.latestVersion;
|
||||
|
||||
// Check if user is on known unstable version - inform but don't force downgrade
|
||||
if (isAboveMaxStable(currentVersion)) {
|
||||
// Check if user is on known faulty version - recommend upgrade
|
||||
if (isVersionFaulty(currentVersion)) {
|
||||
console.log(
|
||||
warn(
|
||||
`CLIProxy Plus v${currentVersion} has known stability issues. ` +
|
||||
`Stable version: v${CLIPROXY_MAX_STABLE_VERSION}`
|
||||
`CLIProxy Plus v${currentVersion} has known bugs (v${CLIPROXY_FAULTY_RANGE.min.replace(/-\d+$/, '')}-${CLIPROXY_FAULTY_RANGE.max.replace(/-\d+$/, '')}). ` +
|
||||
`Upgrade to v${CLIPROXY_MAX_STABLE_VERSION.replace(/-\d+$/, '')} recommended.`
|
||||
)
|
||||
);
|
||||
console.log(
|
||||
info(
|
||||
`Run "ccs cliproxy install ${CLIPROXY_MAX_STABLE_VERSION.replace(/-\d+$/, '')}" to upgrade`
|
||||
)
|
||||
);
|
||||
} else if (isAboveMaxStable(currentVersion)) {
|
||||
// Version newer than max stable (experimental)
|
||||
console.log(
|
||||
warn(
|
||||
`CLIProxy Plus v${currentVersion} is experimental (above stable v${CLIPROXY_MAX_STABLE_VERSION.replace(/-\d+$/, '')})`
|
||||
)
|
||||
);
|
||||
console.log(info('Run "ccs cliproxy install 80" to downgrade, or wait for upstream fix'));
|
||||
}
|
||||
|
||||
if (!updateResult.hasUpdate) return;
|
||||
|
||||
@@ -17,7 +17,7 @@ import {
|
||||
GITHUB_API_ALL_RELEASES,
|
||||
VersionListResult,
|
||||
} from './types';
|
||||
import { CLIPROXY_MAX_STABLE_VERSION } from '../platform-detector';
|
||||
import { CLIPROXY_MAX_STABLE_VERSION, CLIPROXY_FAULTY_RANGE } from '../platform-detector';
|
||||
|
||||
/**
|
||||
* Compare semver versions (true if latest > current)
|
||||
@@ -43,6 +43,17 @@ export function isNewerVersion(latest: string, current: string): boolean {
|
||||
return false; // Equal versions
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if version is within the faulty range (v81-85)
|
||||
* @returns true if version has known critical bugs
|
||||
*/
|
||||
export function isVersionFaulty(version: string): boolean {
|
||||
const { min, max } = CLIPROXY_FAULTY_RANGE;
|
||||
const atOrAboveMin = !isNewerVersion(min, version); // version >= min
|
||||
const atOrBelowMax = !isNewerVersion(version, max); // version <= max
|
||||
return atOrAboveMin && atOrBelowMax;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch latest version from GitHub API
|
||||
*/
|
||||
@@ -123,9 +134,9 @@ export async function fetchAllVersions(verbose = false): Promise<VersionListResu
|
||||
|
||||
const latest = versions[0] || '';
|
||||
|
||||
// Find latest stable (not newer than max stable)
|
||||
// Find latest stable (not newer than max stable AND not in faulty range)
|
||||
const latestStable =
|
||||
versions.find((v) => !isNewerVersion(v, CLIPROXY_MAX_STABLE_VERSION)) ||
|
||||
versions.find((v) => !isNewerVersion(v, CLIPROXY_MAX_STABLE_VERSION) && !isVersionFaulty(v)) ||
|
||||
CLIPROXY_MAX_STABLE_VERSION;
|
||||
|
||||
const result: VersionListResult = {
|
||||
|
||||
@@ -21,6 +21,13 @@ export const CLIPROXY_FALLBACK_VERSION = '6.6.40-0';
|
||||
*/
|
||||
export const CLIPROXY_MAX_STABLE_VERSION = '6.6.80-0';
|
||||
|
||||
/**
|
||||
* Faulty version range - versions with known critical bugs
|
||||
* v81+ have context cancellation bugs causing intermittent 500 errors
|
||||
* When a stable version is found, update MAX_STABLE and set faulty range accordingly
|
||||
*/
|
||||
export const CLIPROXY_FAULTY_RANGE = { min: '6.6.81-0', max: '6.6.999-0' };
|
||||
|
||||
/** @deprecated Use CLIPROXY_FALLBACK_VERSION instead */
|
||||
export const CLIPROXY_VERSION = CLIPROXY_FALLBACK_VERSION;
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ import {
|
||||
import { getPortProcess, isCLIProxyProcess } from '../../utils/port-utils';
|
||||
import type { HealthCheck } from './types';
|
||||
import { CLIPROXY_MAX_STABLE_VERSION } from '../../cliproxy/platform-detector';
|
||||
import { isNewerVersion } from '../../cliproxy/binary/version-checker';
|
||||
import { isNewerVersion, isVersionFaulty } from '../../cliproxy/binary/version-checker';
|
||||
|
||||
/**
|
||||
* Check CLIProxy binary installation
|
||||
@@ -26,17 +26,30 @@ export function checkCliproxyBinary(): HealthCheck {
|
||||
const version = getInstalledCliproxyVersion();
|
||||
const binaryPath = getCLIProxyPath();
|
||||
|
||||
// Check if version is in faulty range (v81-85)
|
||||
const isFaulty = isVersionFaulty(version);
|
||||
// Check if version exceeds stable cap
|
||||
const isUnstable = isNewerVersion(version, CLIPROXY_MAX_STABLE_VERSION);
|
||||
|
||||
if (isFaulty) {
|
||||
return {
|
||||
id: 'cliproxy-binary',
|
||||
name: 'CLIProxy Binary',
|
||||
status: 'warning',
|
||||
message: `v${version} (faulty)`,
|
||||
details: binaryPath,
|
||||
fix: `Upgrade: ccs cliproxy install ${CLIPROXY_MAX_STABLE_VERSION.replace(/-\d+$/, '')}`,
|
||||
};
|
||||
}
|
||||
|
||||
if (isUnstable) {
|
||||
return {
|
||||
id: 'cliproxy-binary',
|
||||
name: 'CLIProxy Binary',
|
||||
status: 'warning',
|
||||
message: `v${version} (unstable)`,
|
||||
message: `v${version} (experimental)`,
|
||||
details: binaryPath,
|
||||
fix: `Downgrade: ccs cliproxy install ${CLIPROXY_MAX_STABLE_VERSION.replace(/-\d+$/, '')}`,
|
||||
fix: `Stable: ccs cliproxy install ${CLIPROXY_MAX_STABLE_VERSION.replace(/-\d+$/, '')}`,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -26,8 +26,15 @@ import {
|
||||
getInstalledCliproxyVersion,
|
||||
installCliproxyVersion,
|
||||
} from '../../cliproxy/binary-manager';
|
||||
import { fetchAllVersions, isNewerVersion } from '../../cliproxy/binary/version-checker';
|
||||
import { CLIPROXY_MAX_STABLE_VERSION } from '../../cliproxy/platform-detector';
|
||||
import {
|
||||
fetchAllVersions,
|
||||
isNewerVersion,
|
||||
isVersionFaulty,
|
||||
} from '../../cliproxy/binary/version-checker';
|
||||
import {
|
||||
CLIPROXY_MAX_STABLE_VERSION,
|
||||
CLIPROXY_FAULTY_RANGE,
|
||||
} from '../../cliproxy/platform-detector';
|
||||
|
||||
const router = Router();
|
||||
|
||||
@@ -549,6 +556,7 @@ router.get('/versions', async (_req: Request, res: Response): Promise<void> => {
|
||||
...result,
|
||||
currentVersion,
|
||||
maxStableVersion: CLIPROXY_MAX_STABLE_VERSION,
|
||||
faultyRange: CLIPROXY_FAULTY_RANGE,
|
||||
});
|
||||
} catch (error) {
|
||||
res.status(500).json({ error: (error as Error).message });
|
||||
@@ -575,14 +583,24 @@ router.post('/install', async (req: Request, res: Response): Promise<void> => {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if version is unstable
|
||||
const isUnstable = isNewerVersion(version, CLIPROXY_MAX_STABLE_VERSION);
|
||||
// Check if version is faulty (v81-85) or experimental (above max stable)
|
||||
const isFaulty = isVersionFaulty(version);
|
||||
const isExperimental = isNewerVersion(version, CLIPROXY_MAX_STABLE_VERSION);
|
||||
|
||||
if (isUnstable && !force) {
|
||||
if (isFaulty && !force) {
|
||||
res.json({
|
||||
success: false,
|
||||
requiresConfirmation: true,
|
||||
message: `Version ${version} is unstable (above max stable ${CLIPROXY_MAX_STABLE_VERSION}). Set force=true to proceed.`,
|
||||
message: `Version ${version} has known bugs (v${CLIPROXY_FAULTY_RANGE.min.replace(/-\d+$/, '')}-${CLIPROXY_FAULTY_RANGE.max.replace(/-\d+$/, '')}). Set force=true to proceed.`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (isExperimental && !force) {
|
||||
res.json({
|
||||
success: false,
|
||||
requiresConfirmation: true,
|
||||
message: `Version ${version} is experimental (above stable ${CLIPROXY_MAX_STABLE_VERSION.replace(/-\d+$/, '')}). Set force=true to proceed.`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
@@ -599,7 +617,8 @@ router.post('/install', async (req: Request, res: Response): Promise<void> => {
|
||||
res.json({
|
||||
success: true,
|
||||
version,
|
||||
isUnstable,
|
||||
isFaulty,
|
||||
isExperimental,
|
||||
message: `Successfully installed CLIProxy Plus v${version}`,
|
||||
});
|
||||
} catch (error) {
|
||||
|
||||
Reference in New Issue
Block a user