我正在创建一个应用程序,其中有一个 Jenkins Pipeline 负责克隆存储库并在其上运行 megalinter,然后当该过程完成时,megalinter.log 文件将被清理,问题将显示在 Issues.jsx 页面中。直到管道构建过程正在进行时,才会有一个动画,其中显示管道状态,但会看到以下错误
- 即使管道成功触发,几秒钟后也会在控制台日志中看到这些错误。 -即使构建完成,也不会获取任何工件 - 动画的阶段在几秒钟内显示失败,但管道在杰宁斯中触发,所以我不知道是什么导致了这些错误。
我不知道 147 从哪里来,我什至没有使用该网址
我在代码中尝试了这些东西 詹金斯管道
pipeline {
agent any
environment {
NODEJS_HOME = tool name: 'NodeJS', type: 'jenkins.plugins.nodejs.tools.NodeJSInstallation'
PATH = "${env.NODEJS_HOME}/bin:${env.PATH}"
}
parameters {
string(name: 'REPO_URL', defaultValue: '', description: 'URL of the Git repository to analyze')
}
stages {
stage('Clone Repository') {
steps {
script {
try {
git url: "${params.REPO_URL}", branch: 'main'
} catch (Exception e) {
echo "Main branch not found, trying master branch"
git url: "${params.REPO_URL}", branch: 'master'
}
}
}
}
stage('Run MegaLinter') {
steps {
script {
try {
bat "npx mega-linter-runner --flavor cupcake --release v6"
} catch (Exception e) {
echo "MegaLinter found issues, but continuing pipeline"
}
}
}
}
}
post {
always {
archiveArtifacts artifacts: 'megalinter-reports/**', allowEmptyArchive: true
cleanWs()
}
}
}
Issues.jsx页面代码
const Issues = () => {
const { repoName } = useParams();
const [buildStage, setBuildStage] = useState('Initializing');
const [issues, setIssues] = useState([]);
const [filteredIssues, setFilteredIssues] = useState([]);
const [filters, setFilters] = useState({
severity: "",
language: "",
linter: ""
});
useEffect(() => {
let isMounted = true;
let intervalId;
const runPipeline = async () => {
try {
setBuildStage('Triggering Pipeline');
await triggerPipeline({ name: repoName });
if (!isMounted) return;
setBuildStage('Waiting for Build');
let lastBuildNumber = null;
const pollBuildStatus = async () => {
try {
const { isBuilding, result, number } = await checkBuildStatus();
if (number !== lastBuildNumber) {
lastBuildNumber = number;
console.log(`New build started: #${number}`);
}
if (!isBuilding && result) {
clearInterval(intervalId);
if (result === 'SUCCESS') {
if (isMounted) {
setBuildStage('Fetching Results');
const results = await getBuildResults(number);
await processMegaLinterResults(results.megaLinterLog);
setBuildStage('Complete');
}
} else {
if (isMounted) setBuildStage(`Failed: ${result}`);
}
}
} catch (error) {
console.error('Error in pollBuildStatus:', error);
if (isMounted) setBuildStage('Failed: Error checking status');
}
};
intervalId = setInterval(pollBuildStatus, 5000); // Check every 5 seconds
} catch (error) {
console.error('Error in pipeline process:', error);
if (isMounted) setBuildStage('Failed: Error triggering pipeline');
}
};
runPipeline();
return () => {
isMounted = false;
if (intervalId) clearInterval(intervalId);
};
}, [repoName]);
const processMegaLinterResults = (logContent) => {
try {
const processedIssues = [];
const lines = logContent.split('\n');
let currentLinter = '';
let currentFile = '';
for (const line of lines) {
if (line.startsWith('Linter:')) {
currentLinter = line.split(':')[1].trim();
} else if (line.startsWith('File:')) {
currentFile = line.split(':')[1].trim();
} else if (line.includes('|')) {
const [lineNumber, column, level, message] = line.split('|').map(item => item.trim());
processedIssues.push({
message,
linter: currentLinter,
file: currentFile,
severity: level,
line: lineNumber,
column
});
}
}
setIssues(processedIssues);
setFilteredIssues(processedIssues);
} catch (error) {
console.error('Error processing MegaLinter results:', error);
}
};
useEffect(() => {
const filtered = issues.filter(issue =>
(!filters.severity || issue.severity === filters.severity) &&
(!filters.language || issue.language === filters.language) &&
(!filters.linter || issue.linter === filters.linter)
);
setFilteredIssues(filtered);
}, [issues, filters]);
const handleFilterChange = (filterType, value) => {
setFilters(prev => ({ ...prev, [filterType]: value }));
};
return (
<div className='flex'>
<Sidebar>
<SideBarItem link="/user/dashboard" icon={<Home size={20} />} text="Home" alert />
<SideBarItem link={`/user/dashboard/repos/${repoName}`} icon={<BarChart4 size={20} />} text="Details" alert />
<SideBarItem link={`/user/dashboard/repos/${repoName}/issues`} icon={<Bug size={20} />} text="Issues" />
<SideBarItem link={`/user/dashboard/repos/${repoName}/commits`} icon={<GitCommitVertical size={20} />} text="Commits" />
<SideBarItem link={`/user/dashboard/repos/${repoName}/pull-requests`} icon={<GitPullRequestIcon size={20} />} text="Pull Requests" />
<SideBarItem link="/user/dashboard/help" icon={<HandHelping size={20} />} text="Help" />
</Sidebar>
<div className="p-4 flex-grow">
<h1 className="text-2xl font-bold mb-4">Issues of {repoName}</h1>
{buildStage !== 'Complete' ? (
<div className="flex flex-col items-center justify-center h-64">
<div className="loader"></div>
<p className="mt-4">Status: {buildStage}</p>
</div>
) : (
<div className="flex flex-col md:flex-row gap-4">
<div className="flex-grow bg-black shadow-md rounded-lg p-6">
<h2 className="text-xl font-semibold mb-4">Mega Linter Results</h2>
<div className="overflow-x-auto">
<table className="min-w-full bg-black">
<thead className="bg-gray-100">
<tr>
<th className="py-2 px-4 border-b text-left">S.No</th>
<th className="py-2 px-4 border-b text-left">Issues</th>
<th className="py-2 px-4 border-b text-left">Linter</th>
<th className="py-2 px-4 border-b text-left">File</th>
<th className="py-2 px-4 border-b text-left">Severity</th>
</tr>
</thead>
<tbody>
{filteredIssues.map((issue, index) => (
<tr key={index} className={index % 2 === 0 ? 'bg-gray-50' : 'bg-black'}>
<td className="py-2 px-4 border-b">{index + 1}</td>
<td className="py-2 px-4 border-b">{issue.message}</td>
<td className="py-2 px-4 border-b">{issue.linter}</td>
<td className="py-2 px-4 border-b">{issue.file}</td>
<td className="py-2 px-4 border-b">{issue.severity}</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
<div className="w-full md:w-64 bg-black shadow-md rounded-lg p-6">
<h2 className="text-xl font-semibold mb-4">Filters</h2>
<div className="space-y-4">
<div>
<label htmlFor="severity" className="block text-sm font-medium text-gray-700">Severity</label>
<select
id="severity"
className="mt-1 block w-full pl-3 pr-10 py-2 text-base border-gray-300 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm rounded-md"
onChange={(e) => handleFilterChange('severity', e.target.value)}
>
<option value="">Select severity</option>
{['Low', 'Medium', 'High'].map((option) => (
<option key={option} value={option}>{option}</option>
))}
</select>
</div>
<div>
<label htmlFor="language" className="block text-sm font-medium text-gray-700">Language</label>
<select
id="language"
className="mt-1 block w-full pl-3 pr-10 py-2 text-base border-gray-300 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm rounded-md"
onChange={(e) => handleFilterChange('language', e.target.value)}
>
<option value="">Select language</option>
{[...new Set(issues.map(issue => issue.language))].map((option) => (
<option key={option} value={option}>{option}</option>
))}
</select>
</div>
<div>
<label htmlFor="linter" className="block text-sm font-medium text-gray-700">Linter</label>
<select
id="linter"
className="mt-1 block w-full pl-3 pr-10 py-2 text-base border-gray-300 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm rounded-md"
onChange={(e) => handleFilterChange('linter', e.target.value)}
>
<option value="">Select linter</option>
{[...new Set(issues.map(issue => issue.linter))].map((option) => (
<option key={option} value={option}>{option}</option>
))}
</select>
</div>
</div>
</div>
</div>
)}
</div>
</div>
);
};
export default Issues;
控制器功能
export const triggerPipeline = async (repo) => {
try {
if (!repo || !repo.name) {
throw new Error('Invalid repository information');
}
const { data: { user }, error } = await supabase.auth.getUser();
if (error) {
throw new Error('Failed to fetch user data');
}
const githubUsername = user.user_metadata.user_name;
if (!githubUsername) {
throw new Error('GitHub username not found in user metadata');
}
const response = await fetch('http://localhost:8080/job/generate-issues/buildWithParameters', {
method: 'POST',
headers: {
'Authorization': 'Basic ' + btoa('username:password'),
'Content-Type': 'application/x-www-form-urlencoded'
},
body: `REPO_URL=https://github.com/${githubUsername}/${repo.name}.git`,
});
console.log('Response status:', response.status);
console.log('Response headers:', JSON.stringify(Object.fromEntries(response.headers)));
if (response.status === 201 || response.status === 303) {
console.log('Jenkins pipeline triggered successfully');
return { message: 'Pipeline triggered successfully' };
} else {
throw new Error(`Failed to trigger Jenkins pipeline: ${response.status}`);
}
} catch (error) {
console.error('Error triggering Jenkins pipeline:', error);
throw error;
}
};
export const checkBuildStatus = async () => {
const buildUrl = 'http://localhost:8080/job/generate-issues/lastBuild/api/json';
const authHeader = 'Basic ' + btoa('username:password');
try {
const response = await fetch(buildUrl, {
headers: {
'Authorization': authHeader
}
});
if (!response.ok) {
throw new Error(`Failed to fetch build status: ${response.status}`);
}
const buildData = await response.json();
console.log('Build data:', buildData);
return {
isBuilding: buildData.building,
result: buildData.result,
number: buildData.number
};
} catch (error) {
console.error('Error checking build status:', error);
return { isBuilding: false, result: 'ERROR', number: null };
}
};
export const getBuildResults = async (buildNumber) => {
const logUrl = `http://localhost:8080/job/generate-issues/${buildNumber}/artifact/megalinter-reports/megalinter.log`;
const authHeader = 'Basic ' + btoa('username:password');
try {
const response = await fetch(logUrl, {
headers: {
'Authorization': authHeader
}
});
if (!response.ok) {
throw new Error(`Failed to fetch MegaLinter log: ${response.status}`);
}
const megaLinterLog = await response.text();
return { megaLinterLog };
} catch (error) {
console.error('Error fetching MegaLinter log:', error);
throw error;
}
};
当您使用此 API 触发构建时,Jenkins 不会立即启动构建 - 而是将请求排队并告知队列项 URL。 这篇文章可能会有所帮助。