1 Commits
Author SHA1 Message Date
sanspie 3d8e3b2fd3 Web export: the page build key covers the runtime and scripts, so a new engine gets new immutable URLs
ci-platforms / build (amd64, windows, windows-2022) (push) Skipped
ci-platforms / build (arm64, darwin, macos-15) (push) Skipped
ci-platforms / build (amd64, darwin, macos-15-intel) (push) Skipped
ci / release (push) Successful in 5m0s
ci / checks (push) Successful in 3m50s
ci / build (amd64, linux, ubuntu-24.04) (push) Successful in 4m21s
2026-10-22 19:26:12 +03:00
2 changed files with 29 additions and 4 deletions
+20 -3
View File
@@ -3,7 +3,9 @@ package export
import (
"bytes"
"compress/gzip"
"crypto/sha256"
"embed"
"encoding/hex"
"encoding/json"
"html/template"
"io/fs"
@@ -141,6 +143,21 @@ func (j *job) webTitle() string {
return AppName(j.p)
}
// webBuild keys the page's ?v= URLs on the data, the runtime and both scripts, so a new engine gets new URLs under immutable caching.
func (j *job) webBuild(scripts ...[]byte) (string, error) {
wasm, err := os.ReadFile(j.l.binary())
if err != nil {
return "", errs.Wrap(err, errs.VFSIO, "cannot read the web runtime")
}
h := sha256.New()
h.Write([]byte(j.rep.Hash))
h.Write(wasm)
for _, s := range scripts {
h.Write(s)
}
return hex.EncodeToString(h.Sum(nil))[:buildKeyLen], nil
}
func (j *job) packWeb() error {
src, err := wasmExecPath(j.goroot)
if err != nil {
@@ -176,9 +193,9 @@ func (j *job) packWeb() error {
if err := writeFile(filepath.Join(j.l.data, WebFiles), list, WebFiles); err != nil {
return err
}
build := j.rep.Hash
if len(build) > buildKeyLen {
build = build[:buildKeyLen]
build, err := j.webBuild(exec, loader)
if err != nil {
return err
}
var page bytes.Buffer
if err := webIndexTmpl.Execute(&page, webPage{Title: j.webTitle(), Build: build, Icon: j.icon != nil}); err != nil {
+9 -1
View File
@@ -5,6 +5,7 @@ import (
"encoding/json"
"os"
"path/filepath"
"regexp"
"strings"
"testing"
@@ -60,9 +61,16 @@ func TestPackWeb(t *testing.T) {
if !strings.Contains(string(page), "&lt;Game &amp;") || strings.Contains(string(page), "<Game") {
t.Fatal("the title is not escaped")
}
if !strings.Contains(string(page), `data-build="abababababab"`) {
key := regexp.MustCompile(`data-build="([0-9a-f]{12})"`).FindStringSubmatch(string(page))
if key == nil {
t.Fatal("the build key is missing")
}
if err := os.WriteFile(j.l.binary(), append(wasm, '!'), 0o644); err != nil {
t.Fatal(err)
}
if again, err := j.webBuild(); err != nil || again == key[1] {
t.Fatalf("a new runtime kept the build key %s (%v)", again, err)
}
raw, err := os.ReadFile(filepath.Join(j.l.data, WebFiles))
if err != nil {
t.Fatal(err)