const core = require('@actions/core');
const exec = require('@actions/exec');
const fs = require('fs');
const path = require('path');
const { formatReportLines } = require('./display');
const TOOL_URL = 'https://gitcode.com/openlibing/openlibing-api-parser.git';
function deriveContext() {
const repository = process.env.ATOMGIT_REPOSITORY || process.env.GITHUB_REPOSITORY || '';
const repoUrl = process.env.ATOMGIT_REPOSITORYURL
|| process.env.ATOMGIT_REPOSITORY_URL
|| (repository ? `https://gitcode.com/${repository}.git` : '');
const commitId = process.env.ATOMGIT_SHA || process.env.ATOMGIT_WORKFLOW_SHA || process.env.GITHUB_SHA || '';
const mergeId = process.env.MERGE_ID || '';
return { repoUrl, mergeId, commitId };
}
async function run() {
const workspace = process.env.ATOMGIT_WORKSPACE || process.env.GITHUB_WORKSPACE || process.cwd();
const uploadUrl = core.getInput('upload_url') || 'https://apig.openlibing.com:443/openlibing-design-api-management/interface/pr-control/report';
const toolRef = core.getInput('tool_ref') || 'master';
const skipInstall = (core.getInput('skip_install') || 'true').toLowerCase() === 'true';
const exclude = core.getInput('exclude') || '.api-parser-tool';
const debug = (core.getInput('debug') || 'false').toLowerCase() === 'true';
const { repoUrl, mergeId, commitId } = deriveContext();
const toolDir = path.join(workspace, '.api-parser-tool');
const scanSh = path.join(toolDir, 'scripts', 'openlibing_scan.sh');
const cliPy = path.join(toolDir, 'cli.py');
const apiAnalysis = path.join(toolDir, 'tools', 'api_analysis', 'linux-x86_64-ubuntu22-llvm15', 'api_analysis');
const reqPath = path.join(toolDir, 'requirements.txt');
const resultFile = path.join(toolDir, 'results.json');
try {
core.info('API-Miner Start ...');
core.info(`REPO_URL=${repoUrl}`);
core.info(`PR_ID=${mergeId}`);
core.info(`COMMIT=${commitId}`);
if (fs.existsSync(toolDir)) {
fs.rmSync(toolDir, { recursive: true, force: true });
}
core.info(`Cloning api-parser tool: ref=${toolRef}`);
await exec.exec('git', ['clone', '-b', toolRef, TOOL_URL, toolDir]);
for (const f of [scanSh, cliPy, apiAnalysis]) {
try {
fs.chmodSync(f, 0o755);
} catch (e) {
core.warning(`chmod failed for ${f}: ${e.message}`);
}
}
core.info('Installing python dependencies ...');
await exec.exec('pip3', ['install', '--break-system-packages', '-r', reqPath, '-i', 'https://pypi.tuna.tsinghua.edu.cn/simple']);
const scanArgs = [
scanSh,
'--source', workspace,
'--exclude', exclude,
'--output', resultFile,
];
if (skipInstall) {
scanArgs.push('--skip-install');
}
core.info('Running api-scan ...');
const scanExit = await exec.exec('bash', scanArgs, { ignoreReturnCode: true, env: { ...process.env, PYTHON_BIN: 'python3' } });
if (scanExit !== 0) {
core.setFailed(`API-Miner scan failed with exit code ${scanExit}`);
return;
}
core.info('API-Miner scan succeeded, uploading results via OIDC ...');
try {
const body = fs.readFileSync(resultFile, 'utf8');
const { callApig, configure } = require('@openlibing/huaweicloud-oidc-client');
if (debug) configure({ debug: true });
const res = await callApig(
'POST',
uploadUrl,
{ 'repo_url': repoUrl, 'merge_id': mergeId, 'commit_id': commitId },
body
);
if (res.status >= 200 && res.status < 300) {
core.info(`✅ 上报成功! 状态码: ${res.status}`);
} else {
core.warning(`上传返回异常状态码: ${res.status}(不阻断 PR 门禁)`);
}
core.info('📋 响应内容:');
for (const line of formatReportLines(res.data)) {
core.info(line);
}
const envelope = res.data && typeof res.data === 'object' ? res.data : {};
const inner = envelope.data && typeof envelope.data === 'object' ? envelope.data : {};
const ccb = inner.compatibilityCountBody || {};
const incompatible = Number(ccb.incompatible || 0);
const scanResult = String(inner.scanResult || '');
if (incompatible > 0 || scanResult.includes('不通过')) {
core.setFailed(`检测到 ${incompatible} 个不兼容变更,PR 门禁未通过`);
}
} catch (e) {
core.warning(`OIDC 上传失败(不阻断): ${e && e.message ? e.message : e}`);
}
} finally {
try {
if (fs.existsSync(toolDir)) {
fs.rmSync(toolDir, { recursive: true, force: true });
}
} catch (e) {
core.warning(`cleanup failed: ${e.message}`);
}
}
}
run().catch(err => core.setFailed(`Unexpected error: ${err && err.message ? err.message : err}`));