mirror of
https://github.com/praktimarc/kst4contest.git
synced 2026-08-04 01:15:27 +02:00
added Beta and Nightly Download Links to website using nightly.link
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
const REPO = "praktimarc/kst4contest";
|
||||
const API = `https://api.github.com/repos/${REPO}`;
|
||||
const RELEASES_URL = `https://github.com/${REPO}/releases`;
|
||||
|
||||
/**
|
||||
* Beta builds are published by .github/workflows/tagged-release.yml whenever
|
||||
* a tag starting with "beta-" is pushed: it creates a GitHub prerelease with
|
||||
* the same asset naming as a Stable release (based on the tag), except for
|
||||
* the flatpakref, which is named ...beta.flatpakref instead of .flatpakref.
|
||||
*
|
||||
* There is no dedicated "beta" marker in the GitHub API beyond the
|
||||
* prerelease flag, but that flag is exactly what this workflow sets, and
|
||||
* nothing else in this repository publishes prereleases, so it is a safe
|
||||
* signal to use here.
|
||||
*/
|
||||
async function fetchLatestBetaRelease() {
|
||||
const headers = { Accept: "application/vnd.github+json" };
|
||||
|
||||
if (process.env.GITHUB_TOKEN) {
|
||||
headers.Authorization = `Bearer ${process.env.GITHUB_TOKEN}`;
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await fetch(`${API}/releases?per_page=10`, { headers });
|
||||
|
||||
if (!res.ok) {
|
||||
throw new Error(`GitHub API /releases failed: ${res.status}`);
|
||||
}
|
||||
|
||||
const releases = await res.json();
|
||||
|
||||
return releases.find((release) => release.prerelease && !release.draft) || null;
|
||||
} catch (err) {
|
||||
console.warn(
|
||||
`[beta] Could not load the latest beta release. ` +
|
||||
`The Beta tab will show its empty state instead: ${err.message}`
|
||||
);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function unavailable() {
|
||||
return {
|
||||
available: false,
|
||||
releasesUrl: RELEASES_URL,
|
||||
items: []
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the Beta download list from the most recent GitHub prerelease.
|
||||
* If there is currently no Beta release, the Beta tab is expected to show
|
||||
* nothing but an explanatory empty state rather than guessed-at links.
|
||||
*/
|
||||
module.exports = async function () {
|
||||
const release = await fetchLatestBetaRelease();
|
||||
|
||||
if (!release) {
|
||||
return unavailable();
|
||||
}
|
||||
|
||||
const tag = release.tag_name;
|
||||
const assetUrl = (filenameTemplate) =>
|
||||
`https://github.com/${REPO}/releases/download/${tag}/${filenameTemplate.replace(/\{tag\}/g, tag)}`;
|
||||
|
||||
const items = [
|
||||
{
|
||||
os: "Windows",
|
||||
format: "ZIP x64",
|
||||
icon: "🪟",
|
||||
note: "Extract the archive, then start praktiKST.exe. No separate Java installation is required.",
|
||||
url: assetUrl("praktiKST-{tag}-windows-x64.zip")
|
||||
},
|
||||
{
|
||||
os: "Linux",
|
||||
format: "Flatpak (.flatpakref)",
|
||||
icon: "🐧",
|
||||
note: "Installs the beta branch of the Flatpak repo through Flatpak or the desktop software centre.",
|
||||
url: assetUrl("de.x08.KST4Contest.beta.flatpakref")
|
||||
},
|
||||
{
|
||||
os: "Linux",
|
||||
format: "AppImage x86_64",
|
||||
icon: "🐧",
|
||||
note: "Portable build without package installation. Make the downloaded file executable before the first launch.",
|
||||
url: assetUrl("KST4Contest-{tag}-linux-x86_64.AppImage")
|
||||
},
|
||||
{
|
||||
os: "Debian / Ubuntu",
|
||||
format: "DEB amd64",
|
||||
icon: "📦",
|
||||
note: "Native package for Debian, Ubuntu and distributions based on them.",
|
||||
url: assetUrl("KST4Contest-{tag}-debian-amd64.deb")
|
||||
},
|
||||
{
|
||||
os: "Fedora",
|
||||
format: "RPM x86_64",
|
||||
icon: "📦",
|
||||
note: "Native package built for Fedora and compatible RPM-based systems.",
|
||||
url: assetUrl("KST4Contest-{tag}-fedora-x86_64.rpm")
|
||||
},
|
||||
{
|
||||
os: "Arch Linux",
|
||||
format: "pkg.tar.zst",
|
||||
icon: "📦",
|
||||
note: "Release package for direct installation with pacman.",
|
||||
url: assetUrl("KST4Contest-{tag}-archlinux-x86_64.pkg.tar.zst")
|
||||
},
|
||||
{
|
||||
os: "macOS Apple Silicon",
|
||||
format: "DMG arm64",
|
||||
icon: "🍎",
|
||||
note: "Best-effort build for Apple Silicon Macs. Not currently notarized by Apple.",
|
||||
url: assetUrl("KST4Contest-{tag}-macos-arm64.dmg")
|
||||
},
|
||||
{
|
||||
os: "macOS Intel",
|
||||
format: "DMG x86_64",
|
||||
icon: "🍎",
|
||||
note: "Best-effort build for Intel Macs. Not currently notarized by Apple.",
|
||||
url: assetUrl("KST4Contest-{tag}-macos-x86_64.dmg")
|
||||
}
|
||||
];
|
||||
|
||||
return {
|
||||
available: true,
|
||||
releasesUrl: RELEASES_URL,
|
||||
tag,
|
||||
name: release.name,
|
||||
publishedAt: release.published_at,
|
||||
releaseUrl: release.html_url,
|
||||
items
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,191 @@
|
||||
const REPO = "praktimarc/kst4contest";
|
||||
const API = `https://api.github.com/repos/${REPO}`;
|
||||
const WORKFLOW_FILE = "nightly-artifacts.yml";
|
||||
const WORKFLOW_BASENAME = "nightly-artifacts";
|
||||
const BRANCH = "main";
|
||||
const WORKFLOW_RUNS_URL = `https://github.com/${REPO}/actions/workflows/${WORKFLOW_FILE}`;
|
||||
|
||||
/**
|
||||
* nightly.link (https://nightly.link) mirrors the most recent successful
|
||||
* GitHub Actions artifact for a given workflow/branch/artifact-name as a
|
||||
* plain public download, with no GitHub account required. It always follows
|
||||
* the latest successful run on its own, so these URLs stay correct without
|
||||
* this site having to resolve a run ID at build time.
|
||||
*/
|
||||
const NIGHTLY_LINK_BASE = `https://nightly.link/${REPO}/workflows/${WORKFLOW_BASENAME}/${BRANCH}`;
|
||||
|
||||
/**
|
||||
* Static metadata for the artifacts produced by
|
||||
* .github/workflows/nightly-artifacts.yml, keyed by the upload-artifact name
|
||||
* used in that workflow. Anything not listed here (e.g. the raw
|
||||
* flatpak-ostree-repo folder) is skipped on the nightly page.
|
||||
*/
|
||||
const ARTIFACT_INFO = {
|
||||
"windows-zip": {
|
||||
os: "Windows",
|
||||
format: "ZIP x64",
|
||||
icon: "🪟",
|
||||
note: "Extract the archive, then start praktiKST.exe. No separate Java installation is required.",
|
||||
order: 1
|
||||
},
|
||||
"linux-appimage": {
|
||||
os: "Linux",
|
||||
format: "AppImage x86_64",
|
||||
icon: "🐧",
|
||||
note: "Portable build. Make the downloaded file executable before the first launch.",
|
||||
order: 2
|
||||
},
|
||||
flatpakref: {
|
||||
os: "Linux",
|
||||
format: "Flatpak (.flatpakref, nightly branch)",
|
||||
icon: "🐧",
|
||||
note: "Adds the nightly branch of the KST4Contest Flatpak repo. See the installation guide for the flatpak CLI alternative.",
|
||||
order: 3
|
||||
},
|
||||
"linux-debian": {
|
||||
os: "Debian / Ubuntu",
|
||||
format: "DEB amd64",
|
||||
icon: "📦",
|
||||
note: "Native package for Debian, Ubuntu and distributions based on them.",
|
||||
order: 4
|
||||
},
|
||||
"linux-fedora": {
|
||||
os: "Fedora",
|
||||
format: "RPM x86_64",
|
||||
icon: "📦",
|
||||
note: "Native package built for Fedora and compatible RPM-based systems.",
|
||||
order: 5
|
||||
},
|
||||
"linux-arch": {
|
||||
os: "Arch Linux",
|
||||
format: "pkg.tar.zst",
|
||||
icon: "📦",
|
||||
note: "Package for direct installation with pacman.",
|
||||
order: 6
|
||||
},
|
||||
"macos-dmg-macos-latest": {
|
||||
os: "macOS Apple Silicon",
|
||||
format: "DMG arm64",
|
||||
icon: "🍎",
|
||||
note: "Best-effort build for Apple Silicon Macs. Not notarized by Apple.",
|
||||
order: 7
|
||||
},
|
||||
"macos-dmg-macos-15-intel": {
|
||||
os: "macOS Intel",
|
||||
format: "DMG x86_64",
|
||||
icon: "🍎",
|
||||
note: "Best-effort build for Intel Macs. Not notarized by Apple.",
|
||||
order: 8
|
||||
}
|
||||
};
|
||||
|
||||
function buildItems() {
|
||||
return Object.entries(ARTIFACT_INFO)
|
||||
.map(([name, info]) => ({
|
||||
...info,
|
||||
name,
|
||||
url: `${NIGHTLY_LINK_BASE}/${name}.zip`
|
||||
}))
|
||||
.sort((a, b) => a.order - b.order);
|
||||
}
|
||||
|
||||
function authHeaders() {
|
||||
const headers = { Accept: "application/vnd.github+json" };
|
||||
|
||||
if (process.env.GITHUB_TOKEN) {
|
||||
headers.Authorization = `Bearer ${process.env.GITHUB_TOKEN}`;
|
||||
}
|
||||
|
||||
return headers;
|
||||
}
|
||||
|
||||
function formatSize(bytes) {
|
||||
if (bytes < 1024 * 1024) {
|
||||
return `${(bytes / 1024).toFixed(0)} KB`;
|
||||
}
|
||||
|
||||
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Best-effort lookup of the run behind the current nightly.link downloads,
|
||||
* purely to show a commit/date on the page. The download links themselves
|
||||
* (buildItems) do not depend on this succeeding.
|
||||
*/
|
||||
async function fetchLatestRunMeta(headers) {
|
||||
const runsRes = await fetch(
|
||||
`${API}/actions/workflows/${WORKFLOW_FILE}/runs?branch=${BRANCH}&status=success&per_page=1`,
|
||||
{ headers }
|
||||
);
|
||||
|
||||
if (!runsRes.ok) {
|
||||
throw new Error(`workflow runs request failed: ${runsRes.status}`);
|
||||
}
|
||||
|
||||
const runsData = await runsRes.json();
|
||||
const run = runsData.workflow_runs && runsData.workflow_runs[0];
|
||||
|
||||
if (!run) {
|
||||
throw new Error("no successful nightly run found");
|
||||
}
|
||||
|
||||
const meta = {
|
||||
runUrl: run.html_url,
|
||||
commitUrl: `https://github.com/${REPO}/commit/${run.head_sha}`,
|
||||
shortSha: run.head_sha.slice(0, 7),
|
||||
createdAt: run.created_at
|
||||
};
|
||||
|
||||
try {
|
||||
const artifactsRes = await fetch(
|
||||
`${API}/actions/runs/${run.id}/artifacts?per_page=100`,
|
||||
{ headers }
|
||||
);
|
||||
|
||||
if (artifactsRes.ok) {
|
||||
const artifactsData = await artifactsRes.json();
|
||||
const sizeByName = {};
|
||||
|
||||
for (const artifact of artifactsData.artifacts || []) {
|
||||
if (!artifact.expired) {
|
||||
sizeByName[artifact.name] = formatSize(artifact.size_in_bytes);
|
||||
}
|
||||
}
|
||||
|
||||
meta.sizeByName = sizeByName;
|
||||
}
|
||||
} catch {
|
||||
// Sizes are a nice-to-have; the run metadata above is still useful without them.
|
||||
}
|
||||
|
||||
return meta;
|
||||
}
|
||||
|
||||
module.exports = async function () {
|
||||
const items = buildItems();
|
||||
|
||||
let meta = null;
|
||||
|
||||
try {
|
||||
meta = await fetchLatestRunMeta(authHeaders());
|
||||
} catch (err) {
|
||||
console.warn(
|
||||
`[nightly] Could not load latest nightly run metadata (commit/date will be omitted). ` +
|
||||
`Download links are unaffected since they are served by nightly.link: ${err.message}`
|
||||
);
|
||||
}
|
||||
|
||||
if (meta && meta.sizeByName) {
|
||||
for (const item of items) {
|
||||
if (meta.sizeByName[item.name]) {
|
||||
item.size = meta.sizeByName[item.name];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
runsUrl: WORKFLOW_RUNS_URL,
|
||||
meta,
|
||||
items
|
||||
};
|
||||
};
|
||||
@@ -10,6 +10,8 @@
|
||||
--accent: #38bdf8;
|
||||
--accent2: #a855f7;
|
||||
--accent3: #22c55e;
|
||||
--warn: #f59e0b;
|
||||
--danger: #f43f5e;
|
||||
--shadow: 0 24px 80px rgba(0, 0, 0, 0.45);
|
||||
}
|
||||
|
||||
@@ -259,6 +261,7 @@ a:hover {
|
||||
color: #020617;
|
||||
background: linear-gradient(135deg, var(--accent), var(--accent2));
|
||||
font-weight: 800;
|
||||
cursor: pointer;
|
||||
box-shadow:
|
||||
0 16px 44px rgba(56, 189, 248, 0.25),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.35);
|
||||
@@ -463,6 +466,181 @@ a:hover {
|
||||
min-height: 220px;
|
||||
}
|
||||
|
||||
.channel-picker {
|
||||
margin: 34px 0 0;
|
||||
}
|
||||
|
||||
.channel-switch {
|
||||
display: inline-flex;
|
||||
gap: 4px;
|
||||
padding: 5px;
|
||||
margin: 0 0 40px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 999px;
|
||||
background: rgba(2, 6, 23, 0.55);
|
||||
}
|
||||
|
||||
.channel-option {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.channel-option input {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
white-space: nowrap;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.channel-option span {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 11px 22px;
|
||||
border-radius: 999px;
|
||||
font-weight: 800;
|
||||
color: var(--muted);
|
||||
cursor: pointer;
|
||||
transition: color 0.15s ease;
|
||||
}
|
||||
|
||||
.channel-option input:checked + span {
|
||||
color: #020617;
|
||||
background: linear-gradient(135deg, var(--accent), var(--accent2));
|
||||
box-shadow: 0 10px 30px rgba(56, 189, 248, 0.25);
|
||||
}
|
||||
|
||||
.channel-option input:focus-visible + span {
|
||||
outline: 2px solid var(--accent);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.channel-panel {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.channel-picker:has(#channel-stable:checked) .channel-panel[data-channel="stable"],
|
||||
.channel-picker:has(#channel-beta:checked) .channel-panel[data-channel="beta"],
|
||||
.channel-picker:has(#channel-nightly:checked) .channel-panel[data-channel="nightly"] {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.notice-banner {
|
||||
display: flex;
|
||||
gap: 18px;
|
||||
align-items: flex-start;
|
||||
padding: clamp(18px, 3vw, 26px);
|
||||
margin-bottom: 30px;
|
||||
border: 1px solid var(--warn);
|
||||
border-radius: 20px;
|
||||
background: linear-gradient(180deg, rgba(245, 158, 11, 0.14), rgba(245, 158, 11, 0.04));
|
||||
}
|
||||
|
||||
.notice-banner .disclaimer-icon {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.notice-banner h3 {
|
||||
margin: 0 0 6px;
|
||||
font-size: 1.1rem;
|
||||
color: #fde68a;
|
||||
}
|
||||
|
||||
.notice-banner p {
|
||||
margin: 0;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
text-align: center;
|
||||
padding: clamp(30px, 5vw, 54px);
|
||||
}
|
||||
|
||||
.empty-state .disclaimer-icon {
|
||||
font-size: 2.4rem;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.empty-state h3 {
|
||||
margin: 0 0 10px;
|
||||
}
|
||||
|
||||
.empty-state p {
|
||||
color: var(--muted);
|
||||
max-width: 560px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.empty-state .actions {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.disclaimer-banner {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
align-items: flex-start;
|
||||
padding: clamp(22px, 4vw, 34px);
|
||||
margin-bottom: 34px;
|
||||
border: 2px solid var(--danger);
|
||||
border-radius: 22px;
|
||||
background: linear-gradient(180deg, rgba(244, 63, 94, 0.16), rgba(244, 63, 94, 0.05));
|
||||
box-shadow: 0 0 40px rgba(244, 63, 94, 0.16);
|
||||
}
|
||||
|
||||
.disclaimer-icon {
|
||||
flex: none;
|
||||
font-size: 2.6rem;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.disclaimer-banner h3 {
|
||||
margin: 0 0 8px;
|
||||
font-size: 1.35rem;
|
||||
color: #fecdd3;
|
||||
}
|
||||
|
||||
.disclaimer-banner p {
|
||||
margin: 0;
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.disclaimer-banner p + p {
|
||||
margin-top: 10px;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.disclaimer-banner .inline-link {
|
||||
color: #fecdd3;
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.sr-only {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
white-space: nowrap;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.channel-panel .section-heading + .actions {
|
||||
margin: -10px 0 30px;
|
||||
}
|
||||
|
||||
.channel-panel .content-card {
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.screenshot-placeholder {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
@@ -491,14 +669,14 @@ a:hover {
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.manual-content pre {
|
||||
pre {
|
||||
overflow-x: auto;
|
||||
padding: 16px;
|
||||
border-radius: 14px;
|
||||
background: #020617;
|
||||
}
|
||||
|
||||
.manual-content code {
|
||||
code {
|
||||
color: #bae6fd;
|
||||
}
|
||||
|
||||
|
||||
+201
-26
@@ -1,45 +1,214 @@
|
||||
---
|
||||
layout: base.njk
|
||||
title: Download KST4Contest
|
||||
description: Download the latest stable KST4Contest packages for Windows, Linux and macOS. The required Java runtime is included.
|
||||
description: Download the latest stable KST4Contest packages for Windows, Linux and macOS, or switch to a nightly build for testing. The required Java runtime is included.
|
||||
---
|
||||
|
||||
<section class="hero">
|
||||
<p class="badge">Stable release</p>
|
||||
<p class="badge">Stable, Beta & Nightly channels</p>
|
||||
<h1>Download KST4Contest</h1>
|
||||
<p class="lead">
|
||||
Choose the package for your operating system. Official builds are published through GitHub Releases,
|
||||
and the required Java runtime is already included.
|
||||
Choose the package for your operating system. The required Java runtime is already included. Use
|
||||
the switch below to pick between the Stable release, Beta and current Nightly builds.
|
||||
</p>
|
||||
|
||||
<div class="actions">
|
||||
<a class="button" href="https://github.com/praktimarc/kst4contest/releases/latest">Latest release on GitHub</a>
|
||||
<a class="button secondary" href="https://github.com/praktimarc/kst4contest/releases">All releases</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="section">
|
||||
<div class="section-heading">
|
||||
<p class="eyebrow">Packages by platform</p>
|
||||
<h2>Choose the format that fits your system</h2>
|
||||
</div>
|
||||
<div class="channel-picker">
|
||||
<fieldset class="channel-switch">
|
||||
<legend class="sr-only">Choose a build channel</legend>
|
||||
<label class="channel-option">
|
||||
<input type="radio" name="channel" id="channel-stable" checked>
|
||||
<span>Stable release</span>
|
||||
</label>
|
||||
<label class="channel-option">
|
||||
<input type="radio" name="channel" id="channel-beta">
|
||||
<span>Beta{% if beta.available %} · {{ beta.tag }}{% endif %}</span>
|
||||
</label>
|
||||
<label class="channel-option">
|
||||
<input type="radio" name="channel" id="channel-nightly">
|
||||
<span>Nightly builds</span>
|
||||
</label>
|
||||
</fieldset>
|
||||
|
||||
<div class="grid">
|
||||
{% for item in downloads %}
|
||||
<article class="card download-card">
|
||||
<div class="feature-icon">{{ item.icon }}</div>
|
||||
<div class="channel-panel" data-channel="stable">
|
||||
<div class="section-heading">
|
||||
<p class="eyebrow">Packages by platform</p>
|
||||
<h2>Choose the format that fits your system</h2>
|
||||
</div>
|
||||
|
||||
{% if item.recommended %}
|
||||
<p class="badge">Default choice</p>
|
||||
{% endif %}
|
||||
<div class="actions">
|
||||
<a class="button" href="https://github.com/praktimarc/kst4contest/releases/latest">Latest release on GitHub</a>
|
||||
<a class="button secondary" href="https://github.com/praktimarc/kst4contest/releases">All releases</a>
|
||||
</div>
|
||||
|
||||
<h3>{{ item.os }}</h3>
|
||||
<p><strong>{{ item.format }}</strong></p>
|
||||
<p>{{ item.note }}</p>
|
||||
<div class="grid">
|
||||
{% for item in downloads %}
|
||||
<article class="card download-card">
|
||||
<div class="feature-icon">{{ item.icon }}</div>
|
||||
|
||||
<a class="button secondary" href="{{ item.url }}">Download →</a>
|
||||
</article>
|
||||
{% endfor %}
|
||||
{% if item.recommended %}
|
||||
<p class="badge">Default choice</p>
|
||||
{% endif %}
|
||||
|
||||
<h3>{{ item.os }}</h3>
|
||||
<p><strong>{{ item.format }}</strong></p>
|
||||
<p>{{ item.note }}</p>
|
||||
|
||||
<a class="button secondary" href="{{ item.url }}">Download →</a>
|
||||
</article>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="channel-panel" data-channel="beta">
|
||||
{% if beta.available %}
|
||||
<div class="notice-banner">
|
||||
<div class="disclaimer-icon" aria-hidden="true">🧪</div>
|
||||
<div>
|
||||
<h3>Pre-release — do a quick check before a contest</h3>
|
||||
<p>
|
||||
Beta builds have a largely defined feature set and are used for focused testing ahead of
|
||||
a Stable release. They can still contain new defects. If you plan to use one on contest
|
||||
day, try it beforehand.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section-heading">
|
||||
<p class="eyebrow">Current beta</p>
|
||||
<h2>{{ beta.name or beta.tag }} · {{ beta.publishedAt | dateToIso }}</h2>
|
||||
</div>
|
||||
<p class="lead">
|
||||
<a href="{{ beta.releaseUrl }}">View this release on GitHub</a>
|
||||
</p>
|
||||
|
||||
<div class="grid">
|
||||
{% for item in beta.items %}
|
||||
<article class="card download-card">
|
||||
<div class="feature-icon">{{ item.icon }}</div>
|
||||
|
||||
<h3>{{ item.os }}</h3>
|
||||
<p><strong>{{ item.format }}</strong></p>
|
||||
<p>{{ item.note }}</p>
|
||||
|
||||
<a class="button secondary" href="{{ item.url }}">Download →</a>
|
||||
</article>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<div class="card content-card">
|
||||
<h3>Flatpak beta channel</h3>
|
||||
<p>
|
||||
Instead of the .flatpakref download above, Flatpak users can also track the beta channel
|
||||
directly through the CLI once the Stable repo is already added:
|
||||
</p>
|
||||
<pre><code>flatpak install kst4contest de.x08.KST4Contest//beta</code></pre>
|
||||
<p>
|
||||
See the <a href="/manual/en/installation/">installation guide</a> for the full Flatpak setup.
|
||||
</p>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="card content-card empty-state">
|
||||
<div class="disclaimer-icon" aria-hidden="true">🧪</div>
|
||||
<h3>No Beta build right now</h3>
|
||||
<p>
|
||||
There is currently no Beta release. Beta builds are published occasionally for focused
|
||||
testing ahead of a Stable release, tagged separately from Nightly builds. Check back later,
|
||||
or use the Stable release or a Nightly build in the meantime.
|
||||
</p>
|
||||
<div class="actions">
|
||||
<label for="channel-stable" class="button secondary">Switch to Stable</label>
|
||||
<label for="channel-nightly" class="button ghost">Switch to Nightly</label>
|
||||
<a class="button ghost" href="{{ beta.releasesUrl }}">All releases on GitHub</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="channel-panel" data-channel="nightly">
|
||||
<div class="disclaimer-banner">
|
||||
<div class="disclaimer-icon" aria-hidden="true">⚠️</div>
|
||||
<div>
|
||||
<h3>Testing only — not for contest operation</h3>
|
||||
<p>
|
||||
Nightly builds are automated, unreviewed snapshots of the <code>main</code> branch. They can
|
||||
be unstable, contain unfinished features, break your settings or corrupt log data. Do not
|
||||
rely on a nightly build during a contest.
|
||||
</p>
|
||||
<p>
|
||||
Use the <label for="channel-stable" class="inline-link">Stable release</label> for actual
|
||||
operation. Nightly builds exist to test a specific fix or feature ahead of the next release.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section-heading">
|
||||
<p class="eyebrow">
|
||||
{% if nightly.meta %}
|
||||
Latest successful build
|
||||
{% else %}
|
||||
Current nightly build
|
||||
{% endif %}
|
||||
</p>
|
||||
<h2>
|
||||
{% if nightly.meta %}
|
||||
Build {{ nightly.meta.shortSha }} · {{ nightly.meta.createdAt | dateToIso }}
|
||||
{% else %}
|
||||
Always points to the newest successful workflow run
|
||||
{% endif %}
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
{% if nightly.meta %}
|
||||
<p class="lead">
|
||||
From commit <a href="{{ nightly.meta.commitUrl }}"><code>{{ nightly.meta.shortSha }}</code></a> ·
|
||||
<a href="{{ nightly.meta.runUrl }}">view this workflow run on GitHub</a>
|
||||
</p>
|
||||
{% else %}
|
||||
<p class="lead">
|
||||
Commit and date details could not be loaded when this page was built. The download links below
|
||||
are unaffected — nightly.link always resolves to the latest successful run on its own.
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
<div class="grid">
|
||||
{% for item in nightly.items %}
|
||||
<article class="card download-card">
|
||||
<div class="feature-icon">{{ item.icon }}</div>
|
||||
|
||||
<h3>{{ item.os }}</h3>
|
||||
<p><strong>{{ item.format }}</strong></p>
|
||||
<p>{{ item.note }}</p>
|
||||
{% if item.size %}<p><small>{{ item.size }}</small></p>{% endif %}
|
||||
|
||||
<a class="button secondary" href="{{ item.url }}">Download →</a>
|
||||
</article>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<div class="card content-card">
|
||||
<h3>How these downloads work</h3>
|
||||
<p>
|
||||
GitHub Actions build artifacts normally require a signed-in GitHub account to download. The
|
||||
links above go through <a href="https://nightly.link/">nightly.link</a>, a public, open-source
|
||||
proxy that mirrors the most recent successful build with no login required. Each link downloads
|
||||
a <code>.zip</code> file; the actual installer, package or AppImage is inside and needs to be
|
||||
extracted once.
|
||||
</p>
|
||||
<p>
|
||||
Instead of the .flatpakref download, Flatpak users can also track the nightly channel directly
|
||||
through the CLI once the Stable or Beta repo is already added:
|
||||
</p>
|
||||
<pre><code>flatpak install kst4contest de.x08.KST4Contest//nightly</code></pre>
|
||||
<p>
|
||||
See the <a href="/manual/en/installation/">installation guide</a> for how the Stable, Beta and
|
||||
Nightly channels relate to each other, and for the full Flatpak setup. Older or in-progress
|
||||
builds can be browsed on the
|
||||
<a href="{{ nightly.runsUrl }}">nightly workflow run list on GitHub</a>.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -78,3 +247,9 @@ description: Download the latest stable KST4Contest packages for Windows, Linux
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<script>
|
||||
var channelTabByHash = { "#beta": "channel-beta", "#nightly": "channel-nightly" };
|
||||
var channelTab = document.getElementById(channelTabByHash[location.hash]);
|
||||
if (channelTab) { channelTab.checked = true; }
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user