Files
mistralrs-package/ui/src/pages/PackageDetail.tsx
rob thijssen a79eafd70f
Some checks failed
deploy-ui / build-and-deploy (push) Has been cancelled
feat: add prerelease RPM builds from upstream main branch
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>
2026-05-11 14:21:28 +03:00

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} &mdash;{" "}
{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()}{" "}
&mdash; {entry.author}
</small>
<pre className="mb-0 mt-1">{entry.text}</pre>
</div>
))}
</Accordion.Body>
</Accordion.Item>
))}
</Accordion>
</>
)}
</>
);
}