Compare commits

..
4 Commits
Author SHA1 Message Date
Gitea Actions ccaa14896c Release 0.1.52 [auto-version-bump] 2026-10-16 07:45:46 +02:00
Ein Anderssono d26d7f0b55 Run Piper ONNX in a module worker
Build and publish frontend / publish (push) Successful in 46s
2026-10-16 07:45:21 +02:00
Gitea Actions 874b9be841 Release 0.1.51 [auto-version-bump] 2026-10-16 07:39:16 +02:00
Ein Anderssono d3b5308097 Show static service version in frontend footer
Build and publish frontend / publish (push) Successful in 48s
2026-10-16 07:38:51 +02:00
10 changed files with 45 additions and 7 deletions
+1
View File
@@ -3,6 +3,7 @@
- This repository owns only the static, accessible web client and its container/chart packaging. - This repository owns only the static, accessible web client and its container/chart packaging.
- Rust remains the sole authority for game state. The frontend calls the backend through same-origin `/api/...` and `/version` paths. - Rust remains the sole authority for game state. The frontend calls the backend through same-origin `/api/...` and `/version` paths.
- Keep frontend and backend behind `ai-spel.k3s.k0.nu` so session cookies remain same-origin. - Keep frontend and backend behind `ai-spel.k3s.k0.nu` so session cookies remain same-origin.
- The public footer displays frontend, static-service, and API versions; load static-service version from uncached `/static/version.json`.
- `VERSION` is the only release-version source in this repository. Use `scripts/bump-version.py`; do not manually edit chart versions. - `VERSION` is the only release-version source in this repository. Use `scripts/bump-version.py`; do not manually edit chart versions.
- Changes are written locally and built, checked, published, and exercised by Gitea Actions on the existing `large` runner and Kubernetes. Never run apps, tests, containers, images, or binaries locally. Do not create or register runners. - Changes are written locally and built, checked, published, and exercised by Gitea Actions on the existing `large` runner and Kubernetes. Never run apps, tests, containers, images, or binaries locally. Do not create or register runners.
- Never commit secrets, generated credentials, kubeconfigs, or environment files. - Never commit secrets, generated credentials, kubeconfigs, or environment files.
+2
View File
@@ -26,6 +26,8 @@ On iPhone and iPad, Safari requires a user gesture to unlock Web Audio. The fron
The browser and API stay on the same public origin, `https://ai-spel.k3s.k0.nu`. The ingress routes `/` to this frontend service and routes `/api`, `/version`, and `/health` to the Rust service. That keeps the current server-side session cookie same-origin and avoids browser CORS configuration. The browser and API stay on the same public origin, `https://ai-spel.k3s.k0.nu`. The ingress routes `/` to this frontend service and routes `/api`, `/version`, and `/health` to the Rust service. That keeps the current server-side session cookie same-origin and avoids browser CORS configuration.
The public footer displays the frontend, static-service, and API versions. It fetches `/static/version.json` without caching; the static service creates that file from its sole `VERSION` source during the same image build used for the matching Helm chart and release tag.
## Release flow ## Release flow
`VERSION` is the only version source for this repository. Gitea Actions on the existing `large` runner runs the static checks, Helm lint/template, publishes a commit image, bumps the patch version through `scripts/bump-version.py`, creates the matching release commit and Git tag, and publishes the release image, Helm chart, and rendered frontend manifest to Harbor. Argo CD tracks the newest published frontend chart. The image and chart releases use the same version as the commit and tag. `VERSION` is the only version source for this repository. Gitea Actions on the existing `large` runner runs the static checks, Helm lint/template, publishes a commit image, bumps the patch version through `scripts/bump-version.py`, creates the matching release commit and Git tag, and publishes the release image, Helm chart, and rendered frontend manifest to Harbor. Argo CD tracks the newest published frontend chart. The image and chart releases use the same version as the commit and tag.
+1 -1
View File
@@ -1 +1 @@
0.1.50 0.1.52
+2 -2
View File
@@ -2,5 +2,5 @@ apiVersion: v2
name: ai-spel-frontend name: ai-spel-frontend
description: Static web frontend for the Swedish life simulator description: Static web frontend for the Swedish life simulator
type: application type: application
version: 0.1.50 version: 0.1.52
appVersion: "0.1.50" appVersion: "0.1.52"
+13
View File
@@ -0,0 +1,13 @@
# ADR 0001: Run Piper in an ES module worker
## Decision
Bundle the Piper worker as an ES module and start it with `new Worker(url, { type: 'module' })`.
## Context
Piper loads ONNX Runtime Web and its WebAssembly loader dynamically. The current IIFE/classic worker bundle reports `TypeError: "undefined" cannot be parsed as a URL` during WASM backend initialization, even though the pinned same-origin ONNX loader and binary are available. This is consistent with module-relative URL metadata being lost in the classic bundle.
## Consequences
The browser must support module workers. Current supported browser targets already require modern WebAssembly and worker support. CI asserts that the worker is built and started in module mode; production acceptance remains the real deployed browser flow.
+6 -1
View File
@@ -71,7 +71,7 @@
<div id="admin-content" aria-live="polite" aria-busy="false"></div> <div id="admin-content" aria-live="polite" aria-busy="false"></div>
</section> </section>
</main> </main>
<footer id="version-footer">Frontend <span id="frontend-version">__FRONTEND_VERSION__</span> · API <span id="api-version">hämtar version…</span></footer> <footer id="version-footer">Frontend <span id="frontend-version">__FRONTEND_VERSION__</span> · Statik <span id="static-version">hämtar version…</span> · API <span id="api-version">hämtar version…</span></footer>
<script src="/piper-runtime.js?v=__FRONTEND_VERSION__"></script> <script src="/piper-runtime.js?v=__FRONTEND_VERSION__"></script>
<script src="/voice.js?v=__FRONTEND_VERSION__"></script> <script src="/voice.js?v=__FRONTEND_VERSION__"></script>
<script> <script>
@@ -863,6 +863,11 @@
}).catch(() => { }).catch(() => {
document.querySelector('#api-version').textContent = 'otillgänglig'; document.querySelector('#api-version').textContent = 'otillgänglig';
}); });
request('/static/version.json', {cache: 'no-store'}).then(({version}) => {
document.querySelector('#static-version').textContent = version;
}).catch(() => {
document.querySelector('#static-version').textContent = 'otillgänglig';
});
request('/api/me').then(() => location.pathname === '/admin' ? openAdmin() : openGame()).catch(showAuth); request('/api/me').then(() => location.pathname === '/admin' ? openAdmin() : openGame()).catch(showAuth);
</script> </script>
</body> </body>
+3 -1
View File
@@ -6,7 +6,9 @@ const pending = new Map();
function getWorker() { function getWorker() {
if (worker) return worker; if (worker) return worker;
worker = new Worker(workerUrl); // ONNX Runtime resolves its WASM loader relative to the worker module URL.
// Keep the worker as ESM so import.meta.url remains available at runtime.
worker = new Worker(workerUrl, {type: 'module'});
worker.onmessage = event => { worker.onmessage = event => {
const message = event.data || {}; const message = event.data || {};
const request = pending.get(message.id); const request = pending.get(message.id);
+1 -1
View File
@@ -25,7 +25,7 @@ await build({
entryPoints: [resolve(root, 'piper-worker.js')], entryPoints: [resolve(root, 'piper-worker.js')],
outfile: join(workerRoot, 'piper-worker.js'), outfile: join(workerRoot, 'piper-worker.js'),
bundle: true, bundle: true,
format: 'iife', format: 'esm',
platform: 'browser', platform: 'browser',
target: ['es2022'], target: ['es2022'],
external: ['fs', 'path'], external: ['fs', 'path'],
+3 -1
View File
@@ -12,6 +12,8 @@ grep -q 'add_header Cross-Origin-Embedder-Policy "require-corp" always;' nginx/d
grep -q 'add_header Cross-Origin-Resource-Policy "same-origin" always;' nginx/default.conf grep -q 'add_header Cross-Origin-Resource-Policy "same-origin" always;' nginx/default.conf
grep -q 'application/javascript js mjs;' nginx/default.conf grep -q 'application/javascript js mjs;' nginx/default.conf
grep -q "request('/version')" index.html grep -q "request('/version')" index.html
grep -q 'id="static-version">hämtar version…' index.html
grep -q "request('/static/version.json', {cache: 'no-store'})" index.html
grep -Fq "credentials: 'same-origin'" index.html grep -Fq "credentials: 'same-origin'" index.html
grep -q 'id="dialogue" aria-label="Dialoghistorik" role="log" aria-live="polite"' index.html grep -q 'id="dialogue" aria-label="Dialoghistorik" role="log" aria-live="polite"' index.html
grep -q 'id="message" name="message"' index.html grep -q 'id="message" name="message"' index.html
@@ -43,7 +45,7 @@ grep -q 'COPY public/tts/piper-wasm-v1/' Dockerfile
grep -q 'build:piper' .gitea/workflows/publish.yaml grep -q 'build:piper' .gitea/workflows/publish.yaml
grep -q 'Piper' piper-runtime.js grep -q 'Piper' piper-runtime.js
grep -q 'sv_SE-nst-medium' piper-worker.js grep -q 'sv_SE-nst-medium' piper-worker.js
grep -q 'new Worker(workerUrl)' piper-runtime.js grep -q "new Worker(workerUrl, {type: 'module'})" piper-runtime.js
grep -q 'worker.js?v=' piper-runtime.js grep -q 'worker.js?v=' piper-runtime.js
grep -q 'worker.onmessageerror' piper-runtime.js grep -q 'worker.onmessageerror' piper-runtime.js
grep -q 'localPiperModelUrl' piper-worker.js grep -q 'localPiperModelUrl' piper-worker.js
+13
View File
@@ -0,0 +1,13 @@
const test = require('node:test');
const assert = require('node:assert/strict');
const {readFile} = require('node:fs/promises');
const {join} = require('node:path');
test('Piper worker is built and started as an ES module', async () => {
const [buildScript, runtime] = await Promise.all([
readFile(join(__dirname, '..', 'scripts', 'build-piper.mjs'), 'utf8'),
readFile(join(__dirname, '..', 'piper-runtime.js'), 'utf8'),
]);
assert.match(buildScript, /outfile: join\(workerRoot, 'piper-worker\.js'\),\s*bundle: true,\s*format: 'esm'/);
assert.match(runtime, /new Worker\(workerUrl, \{type: 'module'\}\)/);
});