Tauri decides dev-vs-production from the `custom-protocol` feature, not from --release, and both workflows build the setup exe with a plain `cargo build`. So cfg(dev) stayed on, the frontend was never embedded, and the window a tester opened loaded devUrl: "localhost refused to connect" on a blank 800x580. The feature is a default of the crate now, with a build.rs guard that fails a release build without it, and build.rs tracks setup/dist so a changed window cannot be left out of the binary it is supposed to be in. Behind that, the next wall: /releases/latest answers only with full releases, and every release here is an alpha, so it 404s. The setup app read that as a refused key and the app's Stable channel read it as a token that cannot see the repository — both have been broken since the first alpha. Each now lists /releases and takes the newest that is neither a draft nor the rolling nightly, which the list really does put first. And check_saved_key threw the saved key away on any failure at all, a dropped connection included. The entry it deletes is the app's own updater token, so only a refusal clears it now. The release notes name the file to hand a tester: six assets on the page and nothing saying which, while the bare NSIS installer is the biggest of them and installs with no key gate and no signature check.
135 lines
6.1 KiB
YAML
135 lines
6.1 KiB
YAML
# A release: push a tag `v1.8.0` (the version in tauri.conf.json, package.json
|
|
# and Cargo.toml must already say 1.8.0) and this builds the Windows installer,
|
|
# signs its updater artifact with the project key, and publishes a GitHub
|
|
# release carrying the installer, its .sig and latest.json — the manifest the
|
|
# app's Stable channel reads (src-tauri/src/updater.rs).
|
|
#
|
|
# Secrets this needs on the repository:
|
|
# TAURI_SIGNING_PRIVATE_KEY the private key (content of soulstream.key)
|
|
# TAURI_SIGNING_PRIVATE_KEY_PASSWORD its password — empty (or unset) for a key without one
|
|
# Optional, to publish somewhere other than this repository (this one is
|
|
# private, and a private release cannot be read by the updater without a
|
|
# token — a separate PUBLIC releases repo is the usual answer):
|
|
# RELEASES_TOKEN a token with contents:write on that repository
|
|
# vars.RELEASES_OWNER / vars.RELEASES_REPO where to publish
|
|
name: release
|
|
|
|
on:
|
|
push:
|
|
tags: ["v*"]
|
|
workflow_dispatch:
|
|
|
|
permissions:
|
|
contents: write
|
|
|
|
jobs:
|
|
windows:
|
|
runs-on: windows-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- uses: actions/setup-node@v4
|
|
with:
|
|
node-version: 22
|
|
cache: npm
|
|
|
|
- uses: dtolnay/rust-toolchain@stable
|
|
|
|
- uses: Swatinem/rust-cache@v2
|
|
with:
|
|
workspaces: "./src-tauri -> ../target"
|
|
|
|
- run: npm ci
|
|
|
|
- name: Read the version
|
|
id: version
|
|
shell: bash
|
|
run: |
|
|
echo "version=$(node -p "require('./src-tauri/tauri.conf.json').version")" >> "$GITHUB_OUTPUT"
|
|
|
|
- name: Build, sign and publish
|
|
uses: tauri-apps/tauri-action@v0
|
|
env:
|
|
GITHUB_TOKEN: ${{ secrets.RELEASES_TOKEN || secrets.GITHUB_TOKEN }}
|
|
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
|
|
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
|
|
with:
|
|
owner: ${{ vars.RELEASES_OWNER || github.repository_owner }}
|
|
repo: ${{ vars.RELEASES_REPO || github.event.repository.name }}
|
|
tagName: v__VERSION__
|
|
releaseName: "SoulStream __VERSION__"
|
|
releaseBody: "See the commits since the last release."
|
|
releaseDraft: false
|
|
prerelease: ${{ contains(github.ref_name, '-') }}
|
|
includeUpdaterJson: true
|
|
args: --bundles nsis
|
|
|
|
# A private repository's assets are reachable only through the API, so the
|
|
# manifest's download URLs become API asset URLs — the app sends its token
|
|
# and `Accept: application/octet-stream` for those (updater.rs). Public
|
|
# repositories serve the same URLs to anyone, so nothing is lost.
|
|
- name: Point the manifest at the API assets
|
|
shell: bash
|
|
env:
|
|
GH_TOKEN: ${{ secrets.RELEASES_TOKEN || secrets.GITHUB_TOKEN }}
|
|
TARGET: ${{ vars.RELEASES_OWNER || github.repository_owner }}/${{ vars.RELEASES_REPO || github.event.repository.name }}
|
|
TAG: v${{ steps.version.outputs.version }}
|
|
run: |
|
|
gh release download "$TAG" --repo "$TARGET" --pattern latest.json --output latest.json --clobber
|
|
gh api "repos/$TARGET/releases/tags/$TAG" > release.json
|
|
node -e '
|
|
const fs = require("fs");
|
|
const m = JSON.parse(fs.readFileSync("latest.json", "utf8"));
|
|
const rel = JSON.parse(fs.readFileSync("release.json", "utf8"));
|
|
for (const p of Object.values(m.platforms)) {
|
|
const name = decodeURIComponent(p.url.split("/").pop());
|
|
const a = rel.assets.find((x) => x.name === name);
|
|
if (!a) { console.error("no asset named", name); process.exit(1); }
|
|
p.url = a.url;
|
|
}
|
|
fs.writeFileSync("latest.json", JSON.stringify(m, null, 2) + "\n");
|
|
console.log(JSON.stringify(m, null, 2));
|
|
'
|
|
gh release upload "$TAG" latest.json --repo "$TARGET" --clobber
|
|
|
|
# The file testers run: our own setup window (setup/). It carries no
|
|
# installer — it downloads the latest release with the tester's key and
|
|
# verifies the signature — so it is built the same for every release.
|
|
- name: Build SoulStream Setup
|
|
shell: bash
|
|
env:
|
|
VERSION: ${{ steps.version.outputs.version }}
|
|
run: |
|
|
npx vite build --config vite.setup.config.js
|
|
cargo build --release -p soulstream-setup
|
|
cp target/release/SoulStream-Setup.exe "SoulStream-Setup-$VERSION.exe"
|
|
ls -la "SoulStream-Setup-$VERSION.exe"
|
|
|
|
# Six files on the page and nothing saying which one a tester runs: the
|
|
# NSIS installer is the biggest and reads like the download, and running
|
|
# it puts the app on with no key gate and no signature check. The notes
|
|
# go on last, once the file they point at is actually up.
|
|
- name: Publish SoulStream Setup
|
|
shell: bash
|
|
env:
|
|
GH_TOKEN: ${{ secrets.RELEASES_TOKEN || secrets.GITHUB_TOKEN }}
|
|
TARGET: ${{ vars.RELEASES_OWNER || github.repository_owner }}/${{ vars.RELEASES_REPO || github.event.repository.name }}
|
|
TAG: v${{ steps.version.outputs.version }}
|
|
VERSION: ${{ steps.version.outputs.version }}
|
|
run: |
|
|
gh release upload "$TAG" "SoulStream-Setup-$VERSION.exe" --repo "$TARGET" --clobber
|
|
{
|
|
echo "### Download \`SoulStream-Setup-$VERSION.exe\`"
|
|
echo
|
|
echo "It asks for your alpha key, fetches this release with it, checks the signature and installs — no admin prompt."
|
|
echo
|
|
echo "The other files are the ones Setup uses:"
|
|
echo
|
|
echo "- \`SoulStream_${VERSION}_x64-setup.exe\` — the bare installer Setup downloads. Running it yourself skips the key and the signature check."
|
|
echo "- \`SoulStream_${VERSION}_x64-setup.exe.sig\` — its signature."
|
|
echo "- \`latest.json\` — the manifest the in-app updater reads."
|
|
echo
|
|
echo "See the commits since the last release."
|
|
} > notes.md
|
|
gh release edit "$TAG" --repo "$TARGET" --notes-file notes.md
|