feat(website): download page for the ISO, homepage with download CTA and 3-step how-it-works
- /download/ and /de/download/: big download button, file name, size and release date, requirements (64-bit PC, UEFI or BIOS, >=16 GB disk that gets erased, USB stick, network cable with internet, second device), five install steps (Etcher / Rufus DD mode / dd, boot menu, Secure Boot off, proksi.local:5000, wizard, pull stick), checksum + verify commands when the release has one, alpha notice, link to all releases - the release data comes from the Forgejo API at build time (partials/release.html): newest release with an .iso asset, falling back to params.version if the lookup fails; getresource cache disabled so a build never pins an old release - homepage: status chip follows the latest release, 'Download ISO' primary CTA + 'Source code' ghost CTA, numbered how-it-works cards, download band before the closer - releases now upload furtka-<ver>.iso.sha256, and release.yml rebuilds the site after publishing so the download page follows each release Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wq1hzUp4ZmCKHDuLKCnu73
This commit is contained in:
parent
4c5bfe3aef
commit
0cad2e05c8
12 changed files with 496 additions and 8 deletions
|
|
@ -57,3 +57,13 @@ jobs:
|
|||
env:
|
||||
FORGEJO_TOKEN: ${{ secrets.FORGEJO_RELEASE_TOKEN }}
|
||||
run: ./scripts/publish-release.sh "${GITHUB_REF_NAME}"
|
||||
|
||||
- name: Refresh furtka.org download page
|
||||
# The download page reads the latest release (ISO, size, date,
|
||||
# checksum) from the Forgejo API at build time, so rebuild the site
|
||||
# now that the release exists. Same runner + bind mounts as
|
||||
# deploy-site.yml. A failure here doesn't touch the release.
|
||||
continue-on-error: true
|
||||
run: |
|
||||
apk add --no-cache hugo rsync
|
||||
./website/deploy-ci.sh
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ Tag per meaningful milestone, not on a calendar. A milestone is: ISO boots, a wi
|
|||
git push origin 26.1-alpha
|
||||
```
|
||||
|
||||
5. **The release workflow does the rest.** `.forgejo/workflows/release.yml` fires on the tag push and runs on the self-hosted runner: `scripts/build-release-tarball.sh` builds the self-update payload (tarball + sha256 + release.json under `dist/`), `iso/build.sh` builds the live-installer ISO, `scripts/publish-release.sh` uploads tarball + sha256 + release.json + ISO to the Forgejo release page. Pre-release is flagged automatically based on the suffix (`-alpha`/`-beta`/`-rc`). ISO build is `continue-on-error`: a flaky ISO step doesn't block the core tarball (the thing boxes need for self-update).
|
||||
5. **The release workflow does the rest.** `.forgejo/workflows/release.yml` fires on the tag push and runs on the self-hosted runner: `scripts/build-release-tarball.sh` builds the self-update payload (tarball + sha256 + release.json under `dist/`), `iso/build.sh` builds the live-installer ISO, `scripts/publish-release.sh` uploads tarball + sha256 + release.json + ISO to the Forgejo release page. Pre-release is flagged automatically based on the suffix (`-alpha`/`-beta`/`-rc`). The ISO gets a `furtka-<ver>.iso.sha256` sidecar, and a last step rebuilds furtka.org so the download page (`/download/`) picks up the new ISO, size, date and checksum from the Forgejo API. ISO build is `continue-on-error`: a flaky ISO step doesn't block the core tarball (the thing boxes need for self-update).
|
||||
|
||||
The release workflow needs one secret set at repo **Settings → Secrets → Actions**:
|
||||
- `FORGEJO_RELEASE_TOKEN` — a PAT with `write:repository` scope.
|
||||
|
|
|
|||
|
|
@ -113,6 +113,15 @@ if [ -f "$ISO" ]; then
|
|||
if ! upload_asset "$ISO"; then
|
||||
echo "warning: ISO upload failed — release published without ISO asset" >&2
|
||||
fi
|
||||
# Checksum sidecar for the download page on furtka.org (and anyone
|
||||
# verifying by hand). Uploaded even when the ISO upload reported a
|
||||
# failure — a 504 there often still lands the file. Written from inside
|
||||
# dist/ so the line carries the bare filename `sha256sum -c` expects.
|
||||
ISO_SHA="$ISO.sha256"
|
||||
(cd "$DIST_DIR" && sha256sum "$(basename "$ISO")" > "$(basename "$ISO_SHA")")
|
||||
if ! upload_asset "$ISO_SHA"; then
|
||||
echo "warning: ISO checksum upload failed" >&2
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "Release $VERSION published: https://$HOST/$REPO/releases/tag/$VERSION"
|
||||
|
|
|
|||
|
|
@ -356,6 +356,214 @@ main.container {
|
|||
.cta--primary { animation: none; }
|
||||
}
|
||||
|
||||
.cta-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 0.9rem 1.1rem;
|
||||
}
|
||||
.cta--ghost {
|
||||
color: var(--fg);
|
||||
border: 1px solid var(--border);
|
||||
background: var(--card-bg);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
.cta--ghost:hover {
|
||||
border-color: var(--accent);
|
||||
color: var(--accent);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
/* ── How it works (home) ─────────────────────────────────── */
|
||||
|
||||
.how-steps {
|
||||
list-style: none;
|
||||
counter-reset: how;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
|
||||
gap: 1rem;
|
||||
}
|
||||
.how-step {
|
||||
counter-increment: how;
|
||||
position: relative;
|
||||
background: var(--card-bg);
|
||||
border: 1px solid var(--card-border);
|
||||
border-radius: 1rem;
|
||||
padding: 1.4rem 1.5rem 1.5rem;
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
.how-step::before {
|
||||
content: counter(how, decimal-leading-zero);
|
||||
display: block;
|
||||
font-family: var(--font-mono);
|
||||
font-weight: 600;
|
||||
font-size: 2.4rem;
|
||||
line-height: 1;
|
||||
letter-spacing: -0.04em;
|
||||
margin-bottom: 0.9rem;
|
||||
background-image: linear-gradient(135deg, var(--accent), var(--fg-muted));
|
||||
-webkit-background-clip: text;
|
||||
background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
.how-step h3 { margin: 0 0 0.35rem; font-size: 1.15rem; letter-spacing: -0.01em; }
|
||||
.how-step p { margin: 0; color: var(--fg-muted); font-size: 0.98rem; line-height: 1.5; }
|
||||
.how-step strong { color: var(--fg); font-weight: 600; }
|
||||
|
||||
/* ── Download band (home, before the closer) ─────────────── */
|
||||
|
||||
.dl-band {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 1.25rem 2rem;
|
||||
margin-block: 4.5rem 0;
|
||||
padding: 2rem;
|
||||
border-radius: 1.25rem;
|
||||
border: 1px solid color-mix(in srgb, var(--accent) 45%, var(--border));
|
||||
background:
|
||||
radial-gradient(120% 140% at 0% 0%, color-mix(in srgb, var(--accent) 14%, transparent), transparent 60%),
|
||||
var(--card-bg);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
.dl-band h2 { margin: 0 0 0.3rem; font-size: 1.5rem; letter-spacing: -0.02em; }
|
||||
.dl-band p { margin: 0; color: var(--fg-muted); max-width: 26rem; }
|
||||
|
||||
/* ── Download page ───────────────────────────────────────── */
|
||||
|
||||
.dl-hero { padding-block: 1rem 1rem; }
|
||||
.dl-hero h1 {
|
||||
font-size: clamp(2.4rem, 7vw, 4rem);
|
||||
line-height: 1;
|
||||
letter-spacing: -0.035em;
|
||||
margin: 0 0 1rem;
|
||||
}
|
||||
.dl-hero .lede {
|
||||
font-size: clamp(1.05rem, 1.6vw, 1.2rem);
|
||||
color: var(--fg-muted);
|
||||
max-width: 36rem;
|
||||
margin: 0 0 2rem;
|
||||
}
|
||||
.dl-card {
|
||||
padding: 1.75rem;
|
||||
border-radius: 1.25rem;
|
||||
border: 1px solid color-mix(in srgb, var(--accent) 45%, var(--border));
|
||||
background:
|
||||
radial-gradient(120% 140% at 100% 0%, color-mix(in srgb, var(--accent) 12%, transparent), transparent 60%),
|
||||
var(--card-bg);
|
||||
}
|
||||
.dl-card .cta svg { flex: none; }
|
||||
.dl-meta {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.75rem 2rem;
|
||||
margin: 1.5rem 0 0;
|
||||
}
|
||||
.dl-meta div { min-width: 0; }
|
||||
.dl-meta dt {
|
||||
font-size: 0.7rem;
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.14em;
|
||||
text-transform: uppercase;
|
||||
color: var(--fg-muted);
|
||||
}
|
||||
.dl-meta dd { margin: 0.15rem 0 0; overflow-wrap: anywhere; }
|
||||
.mono { font-family: var(--font-mono); font-size: 0.92em; }
|
||||
.dl-host { margin: 1.25rem 0 0; font-size: 0.85rem; color: var(--fg-muted); }
|
||||
.dl-alpha {
|
||||
margin: 1.5rem 0 0;
|
||||
padding: 0.9rem 1.1rem;
|
||||
border-left: 3px solid var(--accent);
|
||||
background: var(--bg-subtle);
|
||||
border-radius: 0 0.6rem 0.6rem 0;
|
||||
font-size: 0.95rem;
|
||||
max-width: 40rem;
|
||||
}
|
||||
.dl-section { margin-top: 3.5rem; }
|
||||
.dl-needs { margin: 0; padding-left: 1.2rem; max-width: 40rem; }
|
||||
.dl-needs li { margin-bottom: 0.55rem; }
|
||||
.dl-needs li::marker { color: var(--accent); }
|
||||
.dl-steps {
|
||||
list-style: none;
|
||||
counter-reset: dl;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
}
|
||||
.step-card {
|
||||
counter-increment: dl;
|
||||
display: grid;
|
||||
grid-template-columns: 2.6rem 1fr;
|
||||
gap: 0 1rem;
|
||||
padding: 1.3rem 1.4rem;
|
||||
border: 1px solid var(--card-border);
|
||||
border-radius: 1rem;
|
||||
background: var(--card-bg);
|
||||
}
|
||||
.step-card::before {
|
||||
content: counter(dl);
|
||||
grid-row: span 2;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
width: 2.6rem;
|
||||
height: 2.6rem;
|
||||
border-radius: 999px;
|
||||
background: linear-gradient(135deg, var(--accent), var(--accent-hover));
|
||||
color: #fff;
|
||||
font-weight: 700;
|
||||
}
|
||||
.step-card h2 { margin: 0.35rem 0 0.4rem; font-size: 1.15rem; letter-spacing: -0.01em; }
|
||||
.step-body { min-width: 0; color: var(--fg-muted); }
|
||||
.step-body p { margin: 0 0 0.7rem; }
|
||||
.step-body p:last-child { margin: 0; }
|
||||
.step-body strong { color: var(--fg); font-weight: 600; }
|
||||
.step-body a, .dl-older a, .dl-alpha a { color: var(--accent); }
|
||||
kbd {
|
||||
font-family: var(--font-mono);
|
||||
font-size: 0.85em;
|
||||
padding: 0.05rem 0.4rem;
|
||||
border: 1px solid var(--border);
|
||||
border-bottom-width: 2px;
|
||||
border-radius: 0.3rem;
|
||||
background: var(--bg);
|
||||
color: var(--fg);
|
||||
}
|
||||
.download pre {
|
||||
margin: 0.4rem 0 0;
|
||||
padding: 0.85rem 1rem;
|
||||
border-radius: 0.6rem;
|
||||
background: var(--bg-subtle);
|
||||
border: 1px solid var(--border);
|
||||
overflow-x: auto;
|
||||
font-size: 0.85rem;
|
||||
line-height: 1.55;
|
||||
color: var(--fg);
|
||||
}
|
||||
.download code { font-family: var(--font-mono); }
|
||||
.download :not(pre) > code {
|
||||
padding: 0.05rem 0.3rem;
|
||||
border-radius: 0.3rem;
|
||||
background: var(--bg-subtle);
|
||||
font-size: 0.9em;
|
||||
}
|
||||
.dl-hash code { overflow-wrap: anywhere; white-space: pre-wrap; }
|
||||
.dl-cmds .c { color: var(--fg-muted); }
|
||||
.dl-muted { color: var(--fg-muted); }
|
||||
.dl-older { margin-top: 3rem; }
|
||||
@media (max-width: 30rem) {
|
||||
.dl-card, .dl-band { padding: 1.35rem; }
|
||||
.step-card { grid-template-columns: 1fr; }
|
||||
.step-card::before { grid-row: auto; margin-bottom: 0.6rem; }
|
||||
}
|
||||
|
||||
/* ── Intro paragraph (home, between hero and feature grids) ─ */
|
||||
|
||||
.intro {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
---
|
||||
title: "Furtka"
|
||||
description: "Offenes Heimserver-Betriebssystem — einfach genug für alle."
|
||||
status: "<span class=\"mono\">26.20-alpha</span> — in Arbeit"
|
||||
status_text: "in Arbeit"
|
||||
cta_download: "ISO herunterladen"
|
||||
cta_source: "Quellcode"
|
||||
poll_link: "/de/logo/"
|
||||
poll_text: "Wir suchen ein Logo — hilf uns beim Aussuchen"
|
||||
# features_today / features_next müssen index-parallel zu content/_index.md bleiben.
|
||||
|
|
@ -12,6 +14,16 @@ intro: |
|
|||
Apps, eigenem Namen im Netz, eigenen Daten.
|
||||
|
||||
Das Ziel ist einfach: **dein Vater soll das einrichten können.**
|
||||
steps_label: "So funktioniert's"
|
||||
steps:
|
||||
- title: "Herunterladen"
|
||||
body: "Installer holen und auf einen USB-Stick schreiben."
|
||||
- title: "Installieren"
|
||||
body: "Computer vom Stick starten und dem Assistenten folgen — im Browser auf Handy oder Laptop."
|
||||
- title: "Loslegen"
|
||||
body: "**http://furtka.local** öffnen und Apps mit einem Klick installieren."
|
||||
band_title: "Lust, es auszuprobieren?"
|
||||
band_text: "Kostenlos und quelloffen (AGPL-3.0). Läuft auf 64-Bit-PCs, auch auf alten."
|
||||
features_today_label: "Was heute schon geht"
|
||||
features_today:
|
||||
- "Vom USB-Stick booten und Furtka auf die Festplatte einrichten"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
---
|
||||
title: "Furtka"
|
||||
description: "Open-source home server OS — simple enough for everyone."
|
||||
status: "<span class=\"mono\">26.20-alpha</span> — work in progress"
|
||||
status_text: "work in progress"
|
||||
cta_download: "Download ISO"
|
||||
cta_source: "Source code"
|
||||
poll_link: "/logo/"
|
||||
poll_text: "We're picking a logo — help us choose"
|
||||
# Keep features_today / features_next index-aligned with content/_index.de.md.
|
||||
|
|
@ -12,6 +14,16 @@ intro: |
|
|||
your own name on the network, your own data.
|
||||
|
||||
The goal is simple: **your dad should be able to set this up.**
|
||||
steps_label: "How it works"
|
||||
steps:
|
||||
- title: "Download"
|
||||
body: "Get the installer and write it to a USB stick."
|
||||
- title: "Install"
|
||||
body: "Start the computer from the stick and follow the wizard — in the browser on your phone or laptop."
|
||||
- title: "Use"
|
||||
body: "Open **http://furtka.local** and install apps with one click."
|
||||
band_title: "Ready to try it?"
|
||||
band_text: "Free and open source (AGPL-3.0). Runs on 64-bit PCs, old ones included."
|
||||
features_today_label: "What works today"
|
||||
features_today:
|
||||
- "Boot from USB stick and install Furtka onto the hard drive"
|
||||
|
|
|
|||
40
website/content/download.de.md
Normal file
40
website/content/download.de.md
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
---
|
||||
title: "Furtka herunterladen"
|
||||
description: "Der Furtka-Installer als ISO-Datei: auf einen USB-Stick schreiben, den Computer davon starten und den Heimserver im Browser einrichten."
|
||||
translationKey: "download"
|
||||
url: /de/download/
|
||||
type: download
|
||||
button: "ISO herunterladen"
|
||||
released: "Veröffentlicht"
|
||||
size_label: "Größe"
|
||||
alpha: "Furtka ist **Alpha-Software**: Dinge ändern sich und manches geht kaputt. Installiere es auf einem Computer, dessen Festplatte du nicht brauchst, und sichere alles Wichtige."
|
||||
host_note: "Die Datei kommt von unserem Forgejo-Server (forgejo.sourcegate.online)."
|
||||
need_label: "Was du brauchst"
|
||||
needs:
|
||||
- "Einen **64-Bit-PC** (x86-64) — ein alter Laptop oder Desktop reicht. UEFI- und ältere BIOS-Rechner funktionieren beide."
|
||||
- "Eine Festplatte mit **mindestens 16 GB** für Furtka. **Sie wird komplett gelöscht.**"
|
||||
- "Einen **USB-Stick** mit mindestens 2 GB (wird ebenfalls gelöscht)."
|
||||
- "Ein **Netzwerkkabel** mit Internetzugang — der Installer lädt Pakete herunter, und WLAN wird nicht automatisch eingerichtet."
|
||||
- "Ein **zweites Gerät mit Browser** (Handy oder Laptop) im selben Netzwerk."
|
||||
steps_label: "So installierst du Furtka"
|
||||
steps:
|
||||
- title: "ISO auf einen USB-Stick schreiben"
|
||||
body: |
|
||||
Nimm [balenaEtcher](https://etcher.balena.io/) (Windows, macOS, Linux) oder [Rufus](https://rufus.ie/) unter Windows — wenn Rufus fragt, wähle den **DD-Abbild-Modus**. Unter Linux geht auch `dd` (ersetze `/dev/sdX` durch deinen Stick — lieber zweimal prüfen):
|
||||
|
||||
```
|
||||
sudo dd if={iso} of=/dev/sdX bs=4M status=progress oflag=sync
|
||||
```
|
||||
- title: "Computer vom Stick starten"
|
||||
body: "Stick und Netzwerkkabel einstecken, Computer einschalten und die Taste fürs Bootmenü drücken (oft <kbd>F11</kbd>, <kbd>F12</kbd> oder <kbd>Esc</kbd> — sie wird kurz am Bildschirm angezeigt). Den USB-Stick auswählen. Startet der Computer nicht davon, schalte **Secure Boot** in den Firmware-Einstellungen aus."
|
||||
- title: "Installer von einem anderen Gerät öffnen"
|
||||
body: "Nach kurzer Zeit zeigt der Bildschirm eine Adresse an — meist **http://proksi.local:5000**, dazu die IP-Adresse als Ausweichweg. Öffne sie im Browser auf Handy oder Laptop."
|
||||
- title: "Durch den Assistenten klicken"
|
||||
body: "Drei Seiten: ein Name für deinen Server mit Benutzername und Passwort, die Festplatte und eine letzte Kontrolle. Danach lädt der Installer alles herunter und richtet es ein."
|
||||
- title: "Stick raus, neu starten, fertig"
|
||||
body: "Wenn der Assistent es sagt, den USB-Stick abziehen und neu starten. Dein Server ist dann unter **http://<name>.local** erreichbar — zum Beispiel http://furtka.local."
|
||||
verify_label: "Download prüfen (optional)"
|
||||
verify_intro: "Vergleiche die Prüfsumme der heruntergeladenen Datei mit dieser:"
|
||||
verify_missing: "Für die ISO dieses Releases ist noch keine Prüfsumme veröffentlicht — künftige Releases bringen eine mit."
|
||||
older: "Alle Releases und Änderungen"
|
||||
---
|
||||
40
website/content/download.md
Normal file
40
website/content/download.md
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
---
|
||||
title: "Download Furtka"
|
||||
description: "The Furtka installer as an ISO file: write it to a USB stick, start your computer from it, and set up your home server in the browser."
|
||||
translationKey: "download"
|
||||
url: /download/
|
||||
type: download
|
||||
button: "Download ISO"
|
||||
released: "Released"
|
||||
size_label: "Size"
|
||||
alpha: "Furtka is **alpha software**: things change and some will break. Install it on a computer whose disk you don't need, and keep a backup of anything important."
|
||||
host_note: "The file is served from our Forgejo server (forgejo.sourcegate.online)."
|
||||
need_label: "What you need"
|
||||
needs:
|
||||
- "A **64-bit PC** (x86-64) — an old laptop or desktop is fine. Both UEFI and older BIOS computers work."
|
||||
- "A disk of **at least 16 GB** for Furtka. **It will be erased completely.**"
|
||||
- "A **USB stick** with at least 2 GB (also erased)."
|
||||
- "A **network cable** with internet access — the installer downloads packages, and Wi-Fi isn't set up automatically."
|
||||
- "A **second device with a browser** (phone or laptop) on the same network."
|
||||
steps_label: "How to install"
|
||||
steps:
|
||||
- title: "Write the ISO to a USB stick"
|
||||
body: |
|
||||
Use [balenaEtcher](https://etcher.balena.io/) (Windows, macOS, Linux) or [Rufus](https://rufus.ie/) on Windows — if Rufus asks, pick **DD image mode**. On Linux you can also use `dd` (replace `/dev/sdX` with your stick — double-check it):
|
||||
|
||||
```
|
||||
sudo dd if={iso} of=/dev/sdX bs=4M status=progress oflag=sync
|
||||
```
|
||||
- title: "Start the computer from the stick"
|
||||
body: "Plug in the stick and the network cable, switch the computer on and press the one-time boot menu key (often <kbd>F11</kbd>, <kbd>F12</kbd> or <kbd>Esc</kbd> — it flashes briefly on screen). Pick the USB stick. If the computer refuses to start from it, switch off **Secure Boot** in the firmware settings."
|
||||
- title: "Open the installer from another device"
|
||||
body: "After a short while the screen shows an address — usually **http://proksi.local:5000**, plus the IP address as a fallback. Open it in a browser on your phone or laptop."
|
||||
- title: "Click through the wizard"
|
||||
body: "Three pages: a name for your server with your username and password, the disk to install on, and a last check. Then the installer downloads and sets everything up."
|
||||
- title: "Pull the stick, restart, done"
|
||||
body: "When the wizard says so, remove the USB stick and restart. Your server comes up at **http://<name>.local** — for example http://furtka.local."
|
||||
verify_label: "Check the download (optional)"
|
||||
verify_intro: "Compare the checksum of the file you downloaded with this one:"
|
||||
verify_missing: "This release has no published checksum for the ISO yet — future releases include one."
|
||||
older: "All releases and changelogs"
|
||||
---
|
||||
|
|
@ -8,6 +8,11 @@ enableRobotsTXT = true
|
|||
description = "Open-source home server OS — simple enough for everyone."
|
||||
version = "26.20-alpha"
|
||||
contactEmail = "hallo@furtka.org"
|
||||
# Forgejo repo the download page reads its latest release (ISO asset,
|
||||
# size, date, checksum) from at build time. `version` above is only the
|
||||
# fallback when that lookup fails.
|
||||
forgejoURL = "https://forgejo.sourcegate.online"
|
||||
repoPath = "daniel/furtka"
|
||||
|
||||
[markup.goldmark.renderer]
|
||||
unsafe = true
|
||||
|
|
@ -32,5 +37,12 @@ enableRobotsTXT = true
|
|||
[build]
|
||||
writeStats = true
|
||||
|
||||
# The release lookup must be fresh on every build — Hugo's default keeps
|
||||
# remote resources cached forever, which would pin the download page to
|
||||
# whatever release existed on the first build.
|
||||
[caches]
|
||||
[caches.getresource]
|
||||
maxAge = 0
|
||||
|
||||
[minify]
|
||||
minifyOutput = true
|
||||
|
|
|
|||
63
website/layouts/download/single.html
Normal file
63
website/layouts/download/single.html
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
{{ define "main" }}
|
||||
{{- $r := partialCached "release.html" . "release" -}}
|
||||
{{- $file := path.Base $r.url -}}
|
||||
<article class="download">
|
||||
<header class="dl-hero">
|
||||
<p class="status-chip"><span class="mono">{{ $r.version }}</span></p>
|
||||
<h1>{{ .Title }}</h1>
|
||||
<p class="lede">{{ .Description }}</p>
|
||||
|
||||
<div class="dl-card">
|
||||
<a class="cta cta--primary" href="{{ $r.url }}" download>
|
||||
<svg aria-hidden="true" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3v12"/><path d="m7 10 5 5 5-5"/><path d="M5 21h14"/></svg>
|
||||
{{ .Params.button }}
|
||||
</a>
|
||||
<dl class="dl-meta">
|
||||
<div><dt>ISO</dt><dd class="mono">{{ $file }}</dd></div>
|
||||
{{ with $r.size }}<div><dt>{{ $.Params.size_label }}</dt><dd>{{ lang.FormatNumber 1 (div (float .) 1e9) }} GB</dd></div>{{ end }}
|
||||
{{ with $r.date }}<div><dt>{{ $.Params.released }}</dt><dd>{{ time.Format ":date_long" . }}</dd></div>{{ end }}
|
||||
</dl>
|
||||
<p class="dl-host">{{ .Params.host_note }}</p>
|
||||
</div>
|
||||
|
||||
<p class="dl-alpha">{{ .Params.alpha | markdownify }}</p>
|
||||
</header>
|
||||
|
||||
<section class="dl-section">
|
||||
<p class="section-eyebrow">{{ .Params.need_label }}</p>
|
||||
<ul class="dl-needs">
|
||||
{{ range .Params.needs }}<li>{{ . | markdownify }}</li>{{ end }}
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section class="dl-section">
|
||||
<p class="section-eyebrow">{{ .Params.steps_label }}</p>
|
||||
<ol class="dl-steps">
|
||||
{{ range .Params.steps }}
|
||||
<li class="step-card">
|
||||
<h2>{{ .title }}</h2>
|
||||
<div class="step-body">{{ replace .body "{iso}" $file | markdownify }}</div>
|
||||
</li>
|
||||
{{ end }}
|
||||
</ol>
|
||||
</section>
|
||||
|
||||
<section class="dl-section">
|
||||
<p class="section-eyebrow">{{ .Params.verify_label }}</p>
|
||||
{{ with $r.sha256 }}
|
||||
<p>{{ $.Params.verify_intro }}</p>
|
||||
<pre class="dl-hash"><code>{{ . }}</code></pre>
|
||||
<pre class="dl-cmds"><code><span class="c"># Linux</span>
|
||||
sha256sum {{ $file }}
|
||||
<span class="c"># macOS</span>
|
||||
shasum -a 256 {{ $file }}
|
||||
<span class="c"># Windows (PowerShell)</span>
|
||||
Get-FileHash {{ $file }}</code></pre>
|
||||
{{ else }}
|
||||
<p class="dl-muted">{{ $.Params.verify_missing }}</p>
|
||||
{{ end }}
|
||||
</section>
|
||||
|
||||
<p class="dl-older"><a href="{{ $r.all }}">{{ .Params.older }} →</a></p>
|
||||
</article>
|
||||
{{ end }}
|
||||
|
|
@ -1,16 +1,17 @@
|
|||
{{ define "main" }}
|
||||
{{- $r := partialCached "release.html" . "release" -}}
|
||||
{{- $dl := "download/" | relLangURL -}}
|
||||
<article class="home">
|
||||
<header class="hero">
|
||||
{{ with .Params.status }}
|
||||
<p class="status-chip reveal">{{ . | safeHTML }}</p>
|
||||
{{ end }}
|
||||
<p class="status-chip reveal"><span class="mono">{{ $r.version }}</span> — {{ .Params.status_text }}</p>
|
||||
<h1 class="reveal">{{ .Title }}</h1>
|
||||
{{ with site.Params.description }}<p class="lede reveal">{{ . }}</p>{{ end }}
|
||||
<p class="cta-row reveal">
|
||||
<a class="cta cta--primary" href="https://forgejo.sourcegate.online/daniel/furtka/releases">
|
||||
{{ if eq site.Language.Lang "de" }}Neuestes Release{{ else }}Latest release{{ end }}
|
||||
<a class="cta cta--primary" href="{{ $dl }}">
|
||||
{{ .Params.cta_download }}
|
||||
<span aria-hidden="true">→</span>
|
||||
</a>
|
||||
<a class="cta cta--ghost" href="{{ site.Params.forgejoURL }}/{{ site.Params.repoPath }}">{{ .Params.cta_source }}</a>
|
||||
</p>
|
||||
{{ with .Params.poll_link }}
|
||||
<p class="reveal"><a class="home-poll" href="{{ . }}">{{ partial "mark-m.html" (dict "class" "" "id" "hm") }}{{ partial "mark-r.html" (dict "class" "" "id" "hr") }}<span>{{ $.Params.poll_text }} →</span></a></p>
|
||||
|
|
@ -21,6 +22,20 @@
|
|||
<section class="intro">{{ . | markdownify }}</section>
|
||||
{{ end }}
|
||||
|
||||
{{ with .Params.steps }}
|
||||
<section class="feature-section">
|
||||
{{ with $.Params.steps_label }}<p class="section-eyebrow">{{ . }}</p>{{ end }}
|
||||
<ol class="how-steps">
|
||||
{{ range . }}
|
||||
<li class="how-step" data-gsap="card">
|
||||
<h3>{{ .title }}</h3>
|
||||
<p>{{ .body | markdownify }}</p>
|
||||
</li>
|
||||
{{ end }}
|
||||
</ol>
|
||||
</section>
|
||||
{{ end }}
|
||||
|
||||
{{ if .Params.features_today }}
|
||||
<section class="feature-section">
|
||||
{{ with .Params.features_today_label }}<p class="section-eyebrow">{{ . }}</p>{{ end }}
|
||||
|
|
@ -43,6 +58,16 @@
|
|||
</section>
|
||||
{{ end }}
|
||||
|
||||
{{ with .Params.band_title }}
|
||||
<section class="dl-band" data-gsap="card">
|
||||
<div>
|
||||
<h2>{{ . }}</h2>
|
||||
<p>{{ $.Params.band_text }}</p>
|
||||
</div>
|
||||
<a class="cta cta--primary" href="{{ $dl }}">{{ $.Params.cta_download }} <span aria-hidden="true">→</span></a>
|
||||
</section>
|
||||
{{ end }}
|
||||
|
||||
{{ with .Content }}
|
||||
<section class="prose closer">{{ . }}</section>
|
||||
{{ end }}
|
||||
|
|
|
|||
57
website/layouts/partials/release.html
Normal file
57
website/layouts/partials/release.html
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
{{- /*
|
||||
Latest Furtka release that has an ISO attached, looked up on Forgejo at
|
||||
build time. Returns a dict: version, url (ISO), size (bytes, 0 = unknown),
|
||||
date (time or ""), sha256 ("" when the release has no .iso.sha256), page
|
||||
(release page URL), live (false when the lookup failed and we fell back
|
||||
to site.Params.version). Use via partialCached "release.html" . "release".
|
||||
*/ -}}
|
||||
{{- $repo := printf "%s/%s" site.Params.forgejoURL site.Params.repoPath -}}
|
||||
{{- $v := site.Params.version -}}
|
||||
{{- $out := dict
|
||||
"version" $v
|
||||
"url" (printf "%s/releases/download/%s/furtka-%s.iso" $repo $v $v)
|
||||
"size" 0
|
||||
"date" ""
|
||||
"sha256" ""
|
||||
"page" (printf "%s/releases/tag/%s" $repo $v)
|
||||
"all" (printf "%s/releases" $repo)
|
||||
"live" false
|
||||
-}}
|
||||
{{- $api := printf "%s/api/v1/repos/%s/releases?limit=10" site.Params.forgejoURL site.Params.repoPath -}}
|
||||
{{- with try (resources.GetRemote $api) -}}
|
||||
{{- with .Err -}}
|
||||
{{- warnf "release.html: release lookup failed, using site.Params.version: %s" . -}}
|
||||
{{- else with .Value -}}
|
||||
{{- $done := false -}}
|
||||
{{- range (. | transform.Unmarshal) -}}
|
||||
{{- if and (not $done) (not .draft) -}}
|
||||
{{- $rel := . -}}
|
||||
{{- $iso := "" -}}
|
||||
{{- $sum := "" -}}
|
||||
{{- range .assets -}}
|
||||
{{- if strings.HasSuffix .name ".iso" }}{{ $iso = . }}{{ end -}}
|
||||
{{- if strings.HasSuffix .name ".iso.sha256" }}{{ $sum = . }}{{ end -}}
|
||||
{{- end -}}
|
||||
{{- with $iso -}}
|
||||
{{- $done = true -}}
|
||||
{{- $out = merge $out (dict
|
||||
"version" $rel.tag_name
|
||||
"url" .browser_download_url
|
||||
"size" .size
|
||||
"date" (time.AsTime $rel.published_at)
|
||||
"page" $rel.html_url
|
||||
"live" true) -}}
|
||||
{{- with $sum -}}
|
||||
{{- with try (resources.GetRemote .browser_download_url) -}}
|
||||
{{- with .Value -}}
|
||||
{{- $hash := index (split (trim .Content " \n") " ") 0 -}}
|
||||
{{- if eq (len $hash) 64 }}{{ $out = merge $out (dict "sha256" $hash) }}{{ end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- end -}}
|
||||
{{- return $out -}}
|
||||
Loading…
Add table
Reference in a new issue