Merge PR #495: auto-discover recent Docker Hub dev tags
Merged after resolving conflicts with current main.\n\nValidation:\n- GitHub checks all passing: Test Core, Typecheck, Build Web/Server/Desktop, Repo Drift Check, Schema checks, CodeQL, CodeRabbit.\n- Local: npx vitest run --root . src/web/pages/settings/UpdateCenterSection.test.tsx src/server/services/updateCenterVersionService.test.ts src/server/routes/api/updateCenter.test.ts src/server/services/updateCenterStatusService.test.ts\n- Local: npm run typecheck\n- Local: npm run repo:drift-check
This commit is contained in:
@@ -88,6 +88,12 @@ describe('UpdateCenterSection', () => {
|
||||
displayVersion: 'dev-20260417-f67ade2 @ sha256:bbbbbbbbbbbb',
|
||||
publishedAt: '2026-03-30T10:54:35.591877Z',
|
||||
},
|
||||
{
|
||||
normalizedVersion: 'feature-without-raw-tag',
|
||||
digest: 'sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc',
|
||||
displayVersion: 'feature-without-raw-tag @ sha256:cccccccccccc',
|
||||
publishedAt: '2026-03-30T09:54:35.591877Z',
|
||||
},
|
||||
],
|
||||
helper: {
|
||||
ok: true,
|
||||
@@ -470,6 +476,8 @@ describe('UpdateCenterSection', () => {
|
||||
});
|
||||
await flushMicrotasks();
|
||||
|
||||
expect(collectText(root.root)).toContain('Digest:sha256:bbbbbbbbbbbb');
|
||||
expect(collectText(root.root)).not.toContain('feature-without-raw-tag');
|
||||
expect(collectText(root.root)).toContain('dev-20260417-f67ade2 @ sha256:bbbbbbbbbbbb');
|
||||
|
||||
const deployRecentButton = root.root.find((node) => (
|
||||
|
||||
@@ -221,9 +221,15 @@ function formatImageTarget(tag?: string | null, digest?: string | null) {
|
||||
return '';
|
||||
}
|
||||
|
||||
function normalizeRecentDockerCandidates(input?: UpdateCenterStatus['dockerHubRecentTags'] | null) {
|
||||
type RecentDockerCandidate = NonNullable<NonNullable<UpdateCenterStatus['dockerHubRecentTags']>[number]>;
|
||||
|
||||
function normalizeRecentDockerCandidates(
|
||||
input?: UpdateCenterStatus['dockerHubRecentTags'] | null,
|
||||
): Array<RecentDockerCandidate & { tagName: string }> {
|
||||
if (!Array.isArray(input)) return [];
|
||||
return input.filter((entry) => String(entry?.tagName || entry?.normalizedVersion || '').trim());
|
||||
return input.filter(
|
||||
(entry): entry is RecentDockerCandidate & { tagName: string } => !!String(entry?.tagName || '').trim(),
|
||||
);
|
||||
}
|
||||
|
||||
export default function UpdateCenterSection() {
|
||||
@@ -796,7 +802,7 @@ export default function UpdateCenterSection() {
|
||||
{recentDockerCandidates.length ? (
|
||||
<div style={{ display: 'grid', gap: 8 }}>
|
||||
{recentDockerCandidates.map((candidate) => {
|
||||
const candidateTag = String(candidate.tagName || candidate.normalizedVersion || '').trim();
|
||||
const candidateTag = String(candidate.tagName || '').trim();
|
||||
const candidateDigest = String(candidate.digest || '').trim();
|
||||
const candidateLabel = candidate.displayVersion || candidate.normalizedVersion || candidateTag;
|
||||
const candidateDeployState = describeDockerDeployState({
|
||||
@@ -825,6 +831,11 @@ export default function UpdateCenterSection() {
|
||||
<div style={fieldHintStyle}>
|
||||
最近推送:{formatTaskTime(candidate.publishedAt)}
|
||||
</div>
|
||||
{candidateDigest ? (
|
||||
<div style={{ ...fieldHintStyle, fontFamily: 'var(--font-mono)' }} title={candidateDigest}>
|
||||
Digest:{formatShortDigest(candidateDigest)}
|
||||
</div>
|
||||
) : null}
|
||||
<div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
|
||||
<button
|
||||
type="button"
|
||||
|
||||
Reference in New Issue
Block a user