Some checks failed
deploy-ui / build-and-deploy (push) Has been cancelled
Poll upstream main branch HEAD alongside release tags. When a new commit is detected, build and publish prerelease RPMs to a separate unstable repo at rpm.lair.cafe/fedora/$releasever/$basearch/unstable/. RPM versioning uses the Fedora snapshot convention (e.g. 0.8.1-0.1.20260511git1a2b3c4.fc43) so stable releases automatically supersede any installed prerelease. - RPM spec: conditional Release field via mistralrs_prerelease define - poll-upstream.yml: new check-prerelease job fetches main HEAD + Cargo.toml version - build-prerelease.yml: new workflow for commit-based builds without --locked - UI: fetch both stable/unstable manifests, show channel badges, add unstable repo setup instructions to home page Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
116 lines
3.8 KiB
TypeScript
116 lines
3.8 KiB
TypeScript
import { Accordion, Alert, Badge, Spinner, Table } from "react-bootstrap";
|
|
import { useParams } from "react-router";
|
|
import { usePackages } from "../hooks/usePackages.ts";
|
|
import { CodeBlock } from "../components/CodeBlock.tsx";
|
|
|
|
function formatBytes(bytes: number): string {
|
|
if (bytes < 1024) return `${bytes} B`;
|
|
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
}
|
|
|
|
export function PackageDetail() {
|
|
const { name } = useParams<{ name: string }>();
|
|
const { packages, loading, error } = usePackages();
|
|
|
|
if (loading) return <Spinner animation="border" />;
|
|
if (error) return <Alert variant="danger">Failed to load packages: {error}</Alert>;
|
|
if (packages.length === 0) return <Alert variant="info">No package data available.</Alert>;
|
|
|
|
const versions = packages
|
|
.filter((p) => p.name === name)
|
|
.sort((a, b) => b.buildTime - a.buildTime);
|
|
|
|
if (versions.length === 0)
|
|
return <Alert variant="warning">Package not found: {name}</Alert>;
|
|
|
|
const latest = versions[0];
|
|
const hasUnstable = versions.some((v) => v.channel === "unstable");
|
|
|
|
return (
|
|
<>
|
|
<h1 className="mb-1">{name}</h1>
|
|
<p className="text-body-secondary mb-4">{latest.summary}</p>
|
|
|
|
<CodeBlock language="bash">{`sudo dnf install ${name}`}</CodeBlock>
|
|
|
|
{hasUnstable && (
|
|
<div className="mt-3">
|
|
<CodeBlock language="bash">
|
|
{`# install latest unstable version\nsudo dnf --enablerepo=lair-cafe-unstable install ${name}`}
|
|
</CodeBlock>
|
|
</div>
|
|
)}
|
|
|
|
<h2 className="mt-4 mb-3">
|
|
Versions <Badge bg="secondary">{versions.length}</Badge>
|
|
</h2>
|
|
|
|
<Table striped hover responsive>
|
|
<thead>
|
|
<tr>
|
|
<th>Version</th>
|
|
<th>Channel</th>
|
|
<th>Size</th>
|
|
<th>Built</th>
|
|
<th>Download</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{versions.map((pkg) => (
|
|
<tr key={`${pkg.version}-${pkg.release}-${pkg.channel}`}>
|
|
<td>
|
|
{pkg.version}-{pkg.release}
|
|
</td>
|
|
<td>
|
|
<Badge bg={pkg.channel === "stable" ? "success" : "warning"}>
|
|
{pkg.channel}
|
|
</Badge>
|
|
</td>
|
|
<td>{formatBytes(pkg.size)}</td>
|
|
<td>{new Date(pkg.buildTime * 1000).toLocaleDateString()}</td>
|
|
<td>
|
|
<a href={`${pkg.baseUrl}/${pkg.rpmFilename}`}>
|
|
{pkg.rpmFilename}
|
|
</a>
|
|
</td>
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</Table>
|
|
|
|
{versions.some((v) => v.changelog.length > 0) && (
|
|
<>
|
|
<h2 className="mt-4 mb-3">Changelog</h2>
|
|
<Accordion>
|
|
{versions
|
|
.filter((v) => v.changelog.length > 0)
|
|
.map((pkg) => (
|
|
<Accordion.Item
|
|
key={`${pkg.version}-${pkg.release}`}
|
|
eventKey={`${pkg.version}-${pkg.release}`}
|
|
>
|
|
<Accordion.Header>
|
|
{pkg.version}-{pkg.release} —{" "}
|
|
{new Date(pkg.buildTime * 1000).toLocaleDateString()}
|
|
</Accordion.Header>
|
|
<Accordion.Body>
|
|
{pkg.changelog.map((entry, i) => (
|
|
<div key={i} className="mb-3">
|
|
<small className="text-body-secondary">
|
|
{new Date(entry.date * 1000).toLocaleDateString()}{" "}
|
|
— {entry.author}
|
|
</small>
|
|
<pre className="mb-0 mt-1">{entry.text}</pre>
|
|
</div>
|
|
))}
|
|
</Accordion.Body>
|
|
</Accordion.Item>
|
|
))}
|
|
</Accordion>
|
|
</>
|
|
)}
|
|
</>
|
|
);
|
|
}
|