Windows MSI installer next to the portable ZIP (#61) (#111)

* build: configure the Windows MSI installer in the Compose windows block (#61)

Add the jpackage/WiX options for a per-user MSI next to the existing
icon: a fixed upgradeUuid so Windows Installer replaces an installed
KST4Contest instead of installing side by side, a Start menu group and
desktop shortcut, no UAC prompt, and a directory chooser. The portable
ZIP (createDistributable) is unaffected; the MSI is produced only by
the tagged-release workflow.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* ci: build and attach a Windows MSI installer for tagged releases (#61)

The Windows job of tagged-release.yml now runs packageMsi after the
portable ZIP, renames the result to KST4Contest-<tag>-windows-x64.msi
(consistent with the other platforms; the ZIP keeps its praktiKST name)
and uploads it as the windows-msi artifact, which the release job
downloads and attaches next to the ZIP. The nightly workflow stays
ZIP-only because a nightly MSI would always carry the same version.

The website download data (downloads.js, beta.js) gets an MSI card that
is shown only when the release actually has that asset, so the page
never links to a file that does not exist for an older release.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* docs: describe the Windows MSI installer next to the portable ZIP (#61)

The German and English installation manuals list the MSI in the package
table, describe both Windows forms (installer: per user, Start menu
entry, desktop shortcut, chosen directory, in-place upgrade, removal via
the Windows apps list; ZIP: portable) and the shared data directory
%USERPROFILE%\.praktiKST, and add the MSI path to the Windows update
section. Changelog entries for v1.50 and a note in PROJECT_CONTEXT.md
record the fixed upgrade UUID and the tags-only decision.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* ci: build the Windows MSI for stable tags only (#61)

A beta MSI would carry the same composePackageVersion as the following
stable release. jpackage's MSI upgrade table only accepts strictly higher
versions, so Windows Installer would refuse the stable over an installed
beta and the tester would have to uninstall first. Betas therefore stay
ZIP-only like nightlies; the build, upload and download steps are skipped
for beta- tags, and the release action tolerates the missing asset path.
Manual, changelog and project context say so.

Raised by the Codex review on #111.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Rsclub2_2andClaude Fable 5.1 authored and GitHub committed 2026-10-11 12:27:43 +02:00
1 parent 5491cbcf8b
commit 2eb1ba22cf
9 files changed
+189 -17

No files matched your search

+16
View File
@@ -82,6 +82,21 @@ module.exports = async function () {
const assetUrl = (filenameTemplate) =>
`https://github.com/${REPO}/releases/download/${tag}/${filenameTemplate.replace(/\{tag\}/g, tag)}`;
// The MSI installer exists for tags from version 1.50 onwards (issue #61);
// an older beta release has no such asset and gets no dead link.
const assetNames = (release.assets || []).map((asset) => asset.name);
const windowsMsi = assetNames.includes(`KST4Contest-${tag}-windows-x64.msi`)
? [
{
os: "Windows",
format: "MSI installer x64",
icon: "🪟",
note: "Installs for the current user without administrator rights, with a Start menu entry and a desktop shortcut. Settings are shared with the ZIP version.",
url: assetUrl("KST4Contest-{tag}-windows-x64.msi")
}
]
: [];
const items = [
{
os: "Windows",
@@ -90,6 +105,7 @@ module.exports = async function () {
note: "Extract the archive, then start praktiKST.exe. No separate Java installation is required.",
url: assetUrl("praktiKST-{tag}-windows-x64.zip")
},
...windowsMsi,
{
os: "Linux",
format: "Flatpak (.flatpakref)",
+37 -3
View File
@@ -2,7 +2,11 @@ const REPO = "praktimarc/kst4contest";
const API = `https://api.github.com/repos/${REPO}`;
const LATEST_RELEASE_URL = `https://github.com/${REPO}/releases/latest`;
async function fetchLatestReleaseTag() {
/**
* Loads the latest Stable release: its tag and the names of its assets.
* Returns null if the GitHub API was unavailable.
*/
async function fetchLatestRelease() {
const headers = { Accept: "application/vnd.github+json" };
if (process.env.GITHUB_TOKEN) {
@@ -26,7 +30,10 @@ async function fetchLatestReleaseTag() {
);
}
return release.tag_name;
return {
tag: release.tag_name,
assetNames: (release.assets || []).map((asset) => asset.name)
};
} catch (err) {
console.warn(
`[downloads] Could not load the latest release tag. ` +
@@ -61,10 +68,36 @@ function createAssetUrl(latestTag, filenameTemplate) {
* .github/workflows/tagged-release.yml.
*/
module.exports = async function () {
const latestTag = await fetchLatestReleaseTag();
const release = await fetchLatestRelease();
const latestTag = release ? release.tag : null;
const assetUrl = (filenameTemplate) =>
createAssetUrl(latestTag, filenameTemplate);
/*
* The MSI installer exists for tagged releases from version 1.50 onwards
* (issue #61). Until a Stable release carries one, its card is omitted
* rather than pointing at a file that does not exist. Without API data
* the card is kept: its link then opens the release page like all others.
*/
const hasAsset = (filenameTemplate) =>
!release ||
release.assetNames.includes(
filenameTemplate.replace(/\{tag\}/g, release.tag)
);
const windowsMsi = hasAsset("KST4Contest-{tag}-windows-x64.msi")
? [
{
os: "Windows",
format: "MSI installer x64",
icon: "🪟",
recommended: false,
note: "Installs for the current user without administrator rights, with a Start menu entry and a desktop shortcut. Settings are shared with the ZIP version.",
url: assetUrl("KST4Contest-{tag}-windows-x64.msi")
}
]
: [];
return [
{
os: "Windows",
@@ -74,6 +107,7 @@ module.exports = async function () {
note: "Extract the complete archive, then start praktiKST.exe. No separate Java installation is required.",
url: assetUrl("praktiKST-{tag}-windows-x64.zip")
},
...windowsMsi,
{
os: "Linux",
format: "Flatpak (.flatpakref)",