mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2026-01-11 22:59:03 +08:00
Enhance artifact handling and display for action builds
Extended artifact metadata to include workflow run ID and head SHA. Updated backend to filter artifacts by environment and provide additional metadata. Improved frontend to display new artifact details and adjusted UI for better clarity.
This commit is contained in:
parent
31feec26b5
commit
cb061890d3
@ -850,6 +850,8 @@ export interface ActionArtifact {
|
|||||||
created_at: string;
|
created_at: string;
|
||||||
expires_at: string;
|
expires_at: string;
|
||||||
archive_download_url: string;
|
archive_download_url: string;
|
||||||
|
workflow_run_id?: number;
|
||||||
|
head_sha?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -860,13 +862,14 @@ export async function getLatestActionArtifacts (
|
|||||||
owner: string,
|
owner: string,
|
||||||
repo: string,
|
repo: string,
|
||||||
workflow: string = 'build.yml',
|
workflow: string = 'build.yml',
|
||||||
branch: string = 'main'
|
branch: string = 'main',
|
||||||
|
maxRuns: number = 10
|
||||||
): Promise<ActionArtifact[]> {
|
): Promise<ActionArtifact[]> {
|
||||||
const endpoint = `https://api.github.com/repos/${owner}/${repo}/actions/workflows/${workflow}/runs?branch=${branch}&status=success&per_page=1`;
|
const endpoint = `https://api.github.com/repos/${owner}/${repo}/actions/workflows/${workflow}/runs?branch=${branch}&status=success&per_page=${maxRuns}`;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const runsResponse = await RequestUtil.HttpGetJson<{
|
const runsResponse = await RequestUtil.HttpGetJson<{
|
||||||
workflow_runs: Array<{ id: number; }>;
|
workflow_runs: Array<{ id: number; head_sha: string; created_at: string; }>;
|
||||||
}>(endpoint, 'GET', undefined, {
|
}>(endpoint, 'GET', undefined, {
|
||||||
'User-Agent': 'NapCat',
|
'User-Agent': 'NapCat',
|
||||||
'Accept': 'application/vnd.github.v3+json',
|
'Accept': 'application/vnd.github.v3+json',
|
||||||
@ -877,13 +880,12 @@ export async function getLatestActionArtifacts (
|
|||||||
throw new Error('No successful workflow runs found');
|
throw new Error('No successful workflow runs found');
|
||||||
}
|
}
|
||||||
|
|
||||||
const firstRun = workflowRuns[0];
|
// 获取所有 runs 的 artifacts
|
||||||
if (!firstRun) {
|
const allArtifacts: ActionArtifact[] = [];
|
||||||
throw new Error('No workflow run found');
|
|
||||||
}
|
|
||||||
const runId = firstRun.id;
|
|
||||||
const artifactsEndpoint = `https://api.github.com/repos/${owner}/${repo}/actions/runs/${runId}/artifacts`;
|
|
||||||
|
|
||||||
|
for (const run of workflowRuns) {
|
||||||
|
try {
|
||||||
|
const artifactsEndpoint = `https://api.github.com/repos/${owner}/${repo}/actions/runs/${run.id}/artifacts`;
|
||||||
const artifactsResponse = await RequestUtil.HttpGetJson<{
|
const artifactsResponse = await RequestUtil.HttpGetJson<{
|
||||||
artifacts: ActionArtifact[];
|
artifacts: ActionArtifact[];
|
||||||
}>(artifactsEndpoint, 'GET', undefined, {
|
}>(artifactsEndpoint, 'GET', undefined, {
|
||||||
@ -891,7 +893,20 @@ export async function getLatestActionArtifacts (
|
|||||||
'Accept': 'application/vnd.github.v3+json',
|
'Accept': 'application/vnd.github.v3+json',
|
||||||
});
|
});
|
||||||
|
|
||||||
return artifactsResponse.artifacts || [];
|
if (artifactsResponse.artifacts) {
|
||||||
|
// 为每个 artifact 添加 run 信息
|
||||||
|
for (const artifact of artifactsResponse.artifacts) {
|
||||||
|
artifact.workflow_run_id = run.id;
|
||||||
|
artifact.head_sha = run.head_sha;
|
||||||
|
allArtifacts.push(artifact);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// 单个 run 获取失败,继续下一个
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return allArtifacts;
|
||||||
} catch {
|
} catch {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import { sendSuccess } from '@/napcat-webui-backend/src/utils/response';
|
|||||||
import { WebUiConfig } from '@/napcat-webui-backend/index';
|
import { WebUiConfig } from '@/napcat-webui-backend/index';
|
||||||
import { getLatestTag, getAllTags, compareSemVer } from 'napcat-common/src/helper';
|
import { getLatestTag, getAllTags, compareSemVer } from 'napcat-common/src/helper';
|
||||||
import { getLatestActionArtifacts } from '@/napcat-common/src/mirror';
|
import { getLatestActionArtifacts } from '@/napcat-common/src/mirror';
|
||||||
|
import { NapCatCoreWorkingEnv } from '@/napcat-webui-backend/src/types';
|
||||||
|
|
||||||
export const GetNapCatVersion: RequestHandler = (_, res) => {
|
export const GetNapCatVersion: RequestHandler = (_, res) => {
|
||||||
const data = WebUiDataRuntime.GetNapCatVersion();
|
const data = WebUiDataRuntime.GetNapCatVersion();
|
||||||
@ -32,6 +33,8 @@ export interface VersionInfo {
|
|||||||
createdAt?: string;
|
createdAt?: string;
|
||||||
expiresAt?: string;
|
expiresAt?: string;
|
||||||
size?: number;
|
size?: number;
|
||||||
|
workflowRunId?: number;
|
||||||
|
headSha?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -75,8 +78,13 @@ export const getAllReleasesHandler: RequestHandler = async (req, res) => {
|
|||||||
if (includeActions) {
|
if (includeActions) {
|
||||||
try {
|
try {
|
||||||
const artifacts = await getLatestActionArtifacts('NapNeko', 'NapCatQQ', 'build.yml', 'main');
|
const artifacts = await getLatestActionArtifacts('NapNeko', 'NapCatQQ', 'build.yml', 'main');
|
||||||
|
|
||||||
|
// 根据当前工作环境自动过滤对应的 artifact 类型
|
||||||
|
const isFramework = WebUiDataRuntime.getWorkingEnv() === NapCatCoreWorkingEnv.Framework;
|
||||||
|
const targetArtifactName = isFramework ? 'NapCat.Framework' : 'NapCat.Shell';
|
||||||
|
|
||||||
actionVersions = artifacts
|
actionVersions = artifacts
|
||||||
.filter(a => a.name.includes('NapCat'))
|
.filter(a => a.name === targetArtifactName)
|
||||||
.map(a => ({
|
.map(a => ({
|
||||||
tag: `action-${a.id}`,
|
tag: `action-${a.id}`,
|
||||||
type: 'action' as const,
|
type: 'action' as const,
|
||||||
@ -85,6 +93,8 @@ export const getAllReleasesHandler: RequestHandler = async (req, res) => {
|
|||||||
createdAt: a.created_at,
|
createdAt: a.created_at,
|
||||||
expiresAt: a.expires_at,
|
expiresAt: a.expires_at,
|
||||||
size: a.size_in_bytes,
|
size: a.size_in_bytes,
|
||||||
|
workflowRunId: a.workflow_run_id,
|
||||||
|
headSha: a.head_sha,
|
||||||
}));
|
}));
|
||||||
} catch {
|
} catch {
|
||||||
// 忽略 action artifacts 获取失败
|
// 忽略 action artifacts 获取失败
|
||||||
|
|||||||
@ -267,6 +267,8 @@ interface VersionInfo {
|
|||||||
createdAt?: string;
|
createdAt?: string;
|
||||||
expiresAt?: string;
|
expiresAt?: string;
|
||||||
size?: number;
|
size?: number;
|
||||||
|
workflowRunId?: number;
|
||||||
|
headSha?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 版本选择对话框内容
|
// 版本选择对话框内容
|
||||||
@ -513,7 +515,7 @@ const VersionSelectDialogContent: React.FC<VersionSelectDialogProps> = ({
|
|||||||
setSelectedVersion(version || null);
|
setSelectedVersion(version || null);
|
||||||
}}
|
}}
|
||||||
classNames={{
|
classNames={{
|
||||||
trigger: 'h-10',
|
trigger: 'h-auto min-h-10',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{filteredVersions.map((version) => {
|
{filteredVersions.map((version) => {
|
||||||
@ -524,8 +526,9 @@ const VersionSelectDialogContent: React.FC<VersionSelectDialogProps> = ({
|
|||||||
key={version.tag}
|
key={version.tag}
|
||||||
textValue={version.tag}
|
textValue={version.tag}
|
||||||
>
|
>
|
||||||
|
<div className='flex flex-col gap-0.5'>
|
||||||
<div className='flex items-center gap-2'>
|
<div className='flex items-center gap-2'>
|
||||||
<span>{version.tag}</span>
|
<span>{version.type === 'action' && version.artifactName ? version.artifactName : version.tag}</span>
|
||||||
{version.type === 'prerelease' && (
|
{version.type === 'prerelease' && (
|
||||||
<Chip size='sm' color='secondary' variant='flat'>预发布</Chip>
|
<Chip size='sm' color='secondary' variant='flat'>预发布</Chip>
|
||||||
)}
|
)}
|
||||||
@ -539,6 +542,14 @@ const VersionSelectDialogContent: React.FC<VersionSelectDialogProps> = ({
|
|||||||
<Chip size='sm' color='warning' variant='flat'>降级</Chip>
|
<Chip size='sm' color='warning' variant='flat'>降级</Chip>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
{version.type === 'action' && (
|
||||||
|
<div className='text-xs text-default-400'>
|
||||||
|
{version.headSha && <span className='font-mono'>{version.headSha.slice(0, 7)}</span>}
|
||||||
|
{version.createdAt && <span className='ml-2'>{new Date(version.createdAt).toLocaleString()}</span>}
|
||||||
|
{version.size && <span className='ml-2'>{(version.size / 1024 / 1024).toFixed(1)} MB</span>}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|||||||
@ -83,6 +83,8 @@ export default class WebUIManager {
|
|||||||
createdAt?: string;
|
createdAt?: string;
|
||||||
expiresAt?: string;
|
expiresAt?: string;
|
||||||
size?: number;
|
size?: number;
|
||||||
|
workflowRunId?: number;
|
||||||
|
headSha?: string;
|
||||||
}>;
|
}>;
|
||||||
pagination: {
|
pagination: {
|
||||||
page: number;
|
page: number;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user