mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-07-29 13:19:15 +00:00
3da0aa6036
Updates all hardcoded GitHub repository references across 21 files: - package.json: repository URL, bugs URL, homepage, description, author - CONTRIBUTING.md: bug report template URL - SECURITY.md: advisory URL, cosign cert-identity regexp - .github/CODEOWNERS: @AnsoCode -> @studio-saelix/maintainers - .github/workflows/ci.yml: repositories scope (Sencho -> sencho), docs-sync git URL - .github/workflows/cla.yml: path-to-document URL - .github/workflows/docker-publish.yml: cosign verify comment - frontend/**/*.tsx: issues and changelog links (3 components) - frontend/public/.well-known/security.txt: Contact and Policy URLs - security/vex/sencho.openvex.json: @id field - docs/openapi.yaml: license URL - docs/docs.json: navbar and footer GitHub links (5 instances) - docs/security.mdx: advisory and SECURITY.md links - docs/reference/verifying-images.mdx: repo link + cosign regexp + legacy identity note - docs/reference/contact.mdx: issues, LICENSE, advisory, policy, CoC links - docs/reference/security-advisories.mdx: releases link - docs/operations/verifying-images.mdx: cosign regexps and VEX download URL (6 instances) - docs/operations/upgrade.mdx: releases links (2 instances) - backend/src/utils/version-check.ts: GitHub Releases API endpoint CHANGELOG.md intentionally excluded (release-please managed). Legacy cosign identity note added for pre-migration image verification.
77 lines
2.7 KiB
TypeScript
77 lines
2.7 KiB
TypeScript
import semver from 'semver';
|
|
import { CacheService } from '../services/CacheService';
|
|
|
|
/**
|
|
* Fetches the latest Sencho release version from GitHub or Docker Hub.
|
|
* Extracted from index.ts so both the fleet endpoint and MonitorService
|
|
* can share the same lookup logic.
|
|
*/
|
|
|
|
async function fetchFromGitHub(): Promise<string | null> {
|
|
const res = await fetch('https://api.github.com/repos/studio-saelix/sencho/releases/latest', {
|
|
headers: { Accept: 'application/vnd.github+json', 'User-Agent': 'Sencho' },
|
|
signal: AbortSignal.timeout(10000),
|
|
});
|
|
if (!res.ok) return null;
|
|
const data = await res.json() as { tag_name?: string };
|
|
const tag = data.tag_name?.replace(/^v/, '') ?? null;
|
|
return tag && semver.valid(tag) ? tag : null;
|
|
}
|
|
|
|
async function fetchFromDockerHub(): Promise<string | null> {
|
|
const res = await fetch(
|
|
'https://hub.docker.com/v2/repositories/saelix/sencho/tags/?page_size=50&ordering=last_updated',
|
|
{ headers: { 'User-Agent': 'Sencho' }, signal: AbortSignal.timeout(10000) },
|
|
);
|
|
if (!res.ok) return null;
|
|
const data = await res.json() as { results?: { name: string }[] };
|
|
const tags = (data.results ?? [])
|
|
.map(t => t.name)
|
|
.filter(n => semver.valid(n));
|
|
if (tags.length === 0) return null;
|
|
tags.sort(semver.rcompare);
|
|
return tags[0];
|
|
}
|
|
|
|
export async function fetchLatestSenchoVersion(): Promise<string> {
|
|
try {
|
|
const gh = await fetchFromGitHub();
|
|
if (gh) return gh;
|
|
} catch (err) {
|
|
// GitHub API fails for private repos or rate limits; try Docker Hub
|
|
console.warn('[VersionCheck] GitHub fetch failed:', (err as Error).message);
|
|
}
|
|
try {
|
|
const hub = await fetchFromDockerHub();
|
|
if (hub) return hub;
|
|
} catch (err) {
|
|
console.warn('[VersionCheck] Docker Hub fetch failed:', (err as Error).message);
|
|
}
|
|
// Throw so CacheService falls back to a stale value if one exists,
|
|
// and so we do not poison the cache with null.
|
|
throw new Error('Both GitHub and Docker Hub version lookups failed');
|
|
}
|
|
|
|
/**
|
|
* Cached wrapper shared by the Fleet endpoint and MonitorService.
|
|
* CacheService provides TTL, inflight deduplication, and stale-on-error
|
|
* fallback so transient network blips do not cause user-visible gaps.
|
|
*/
|
|
const LATEST_VERSION_CACHE_KEY = 'latest-version';
|
|
const LATEST_VERSION_CACHE_TTL = 30 * 60 * 1000; // 30 minutes
|
|
|
|
export async function getLatestVersion(forceRefresh = false): Promise<string | null> {
|
|
if (forceRefresh) {
|
|
CacheService.getInstance().invalidate(LATEST_VERSION_CACHE_KEY);
|
|
}
|
|
try {
|
|
return await CacheService.getInstance().getOrFetch<string>(
|
|
LATEST_VERSION_CACHE_KEY,
|
|
LATEST_VERSION_CACHE_TTL,
|
|
fetchLatestSenchoVersion,
|
|
);
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|