Skip to content

Commit e17af65

Browse files
authored
ci : add github pages workflow for wasm examples (#2969)
* ci : add github pages workflow for wasm examples This commit adds a github workflow to build and deploy the wasm examples to github pages. The whisper.wasm example is deployed as the main page. This workflow is trigged by a push to master and will deploy the examples to: https://ggerganov.github.io/whisper.cpp/. This requires that the repository has enabled github actions in `Settings` -> `Pages` -> `Build and deployment` -> `Source` be set to `GitHub Actions`. One thing to note is that this commit removes the `talk` example as I'm not sure how this example is built yet. Refs: #2784
1 parent 88d13a1 commit e17af65

File tree

6 files changed

+260
-20
lines changed

6 files changed

+260
-20
lines changed

.github/workflows/examples-wasm.yml

+91
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
name: Examples WASM
2+
on:
3+
push:
4+
branches: ["master"]
5+
6+
workflow_dispatch:
7+
8+
permissions:
9+
contents: read
10+
pages: write
11+
id-token: write
12+
13+
concurrency:
14+
group: "pages"
15+
cancel-in-progress: false
16+
17+
jobs:
18+
deploy-wasm-github-pages:
19+
environment:
20+
name: github-pages
21+
url: ${{ steps.deployment.outputs.page_url }}
22+
runs-on: ubuntu-latest
23+
steps:
24+
- name: Checkout
25+
uses: actions/checkout@v4
26+
27+
- name: Setup Pages
28+
uses: actions/configure-pages@v4
29+
30+
- name: Setup emsdk
31+
uses: mymindstorm/setup-emsdk@v14
32+
33+
- name: Build WASM Examples
34+
# Enable for real build later in whisper.cpp
35+
run: |
36+
mkdir -p build-em && cd build-em
37+
emcmake cmake .. -DCMAKE_BUILD_TYPE=Release
38+
make -j
39+
40+
- name: Create staging directory
41+
run: mkdir -p staging
42+
43+
- name: Create .nojekyll file in staging directory
44+
run: touch staging/.nojekyll
45+
46+
- name: Copy application files
47+
run: |
48+
build_dir=build-em/bin
49+
50+
ls ${build_dir}
51+
52+
# command.wasm
53+
target_dir=staging/command.wasm
54+
mkdir -p ${target_dir}
55+
cp ${build_dir}/command.wasm/{index.html,command.js,helpers.js} ${target_dir}
56+
cp ${build_dir}/libcommand.js ${target_dir}
57+
58+
# bench.wasm
59+
target_dir=staging/bench.wasm
60+
mkdir -p ${target_dir}
61+
cp ${build_dir}/bench.wasm/{index.html,bench.js,helpers.js} ${target_dir}
62+
cp ${build_dir}/libbench.js ${target_dir}
63+
64+
# stream.wasm
65+
target_dir=staging/stream.wasm
66+
mkdir -p ${target_dir}
67+
cp ${build_dir}/stream.wasm/{index.html,stream.js,helpers.js} ${target_dir}
68+
cp ${build_dir}/libstream.js ${target_dir}
69+
70+
# whisper.wasm (this will be the main example page)
71+
target_dir=staging
72+
mkdir -p ${target_dir}
73+
cp ${build_dir}/whisper.wasm/{index.html,main.js,helpers.js} ${target_dir}
74+
cp ${build_dir}/libmain.js ${target_dir}
75+
76+
# Copy Cross-Origin Isolation service worker
77+
cp -v examples/coi-serviceworker.js staging/
78+
79+
- name: List files in staging directory (for debugging)
80+
run: |
81+
echo "Files in staging directory:"
82+
find staging -type f | sort
83+
84+
- name: Upload artifact
85+
uses: actions/upload-pages-artifact@v3
86+
with:
87+
path: ./staging
88+
89+
- name: Deploy to GitHub Pages
90+
id: deployment
91+
uses: actions/deploy-pages@v4

examples/bench.wasm/index-tmpl.html

+6-5
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
overflow-x: scroll;
2525
}
2626
</style>
27+
<script src="../coi-serviceworker.js"></script>
28+
<link rel="icon" href="data:,">
2729
</head>
2830
<body>
2931
<div id="main-container">
@@ -36,11 +38,10 @@
3638
<br><br>
3739

3840
<b>More examples:</b>
39-
<a href="https://whisper.ggerganov.com/">main</a> |
40-
<a href="https://whisper.ggerganov.com/bench">bench</a> |
41-
<a href="https://whisper.ggerganov.com/stream">stream</a> |
42-
<a href="https://whisper.ggerganov.com/command">command</a> |
43-
<a href="https://whisper.ggerganov.com/talk">talk</a> |
41+
<a href="../">main</a> |
42+
<a href="../bench.wasm/">bench</a> |
43+
<a href="../stream.wasm">stream</a> |
44+
<a href="../command.wasm/">command</a> |
4445

4546
<br><br>
4647

examples/coi-serviceworker.js

+146
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
/*! coi-serviceworker v0.1.7 - Guido Zuidhof and contributors, licensed under MIT */
2+
let coepCredentialless = false;
3+
if (typeof window === 'undefined') {
4+
self.addEventListener("install", () => self.skipWaiting());
5+
self.addEventListener("activate", (event) => event.waitUntil(self.clients.claim()));
6+
7+
self.addEventListener("message", (ev) => {
8+
if (!ev.data) {
9+
return;
10+
} else if (ev.data.type === "deregister") {
11+
self.registration
12+
.unregister()
13+
.then(() => {
14+
return self.clients.matchAll();
15+
})
16+
.then(clients => {
17+
clients.forEach((client) => client.navigate(client.url));
18+
});
19+
} else if (ev.data.type === "coepCredentialless") {
20+
coepCredentialless = ev.data.value;
21+
}
22+
});
23+
24+
self.addEventListener("fetch", function (event) {
25+
const r = event.request;
26+
if (r.cache === "only-if-cached" && r.mode !== "same-origin") {
27+
return;
28+
}
29+
30+
const request = (coepCredentialless && r.mode === "no-cors")
31+
? new Request(r, {
32+
credentials: "omit",
33+
})
34+
: r;
35+
event.respondWith(
36+
fetch(request)
37+
.then((response) => {
38+
if (response.status === 0) {
39+
return response;
40+
}
41+
42+
const newHeaders = new Headers(response.headers);
43+
newHeaders.set("Cross-Origin-Embedder-Policy",
44+
coepCredentialless ? "credentialless" : "require-corp"
45+
);
46+
if (!coepCredentialless) {
47+
newHeaders.set("Cross-Origin-Resource-Policy", "cross-origin");
48+
}
49+
newHeaders.set("Cross-Origin-Opener-Policy", "same-origin");
50+
51+
return new Response(response.body, {
52+
status: response.status,
53+
statusText: response.statusText,
54+
headers: newHeaders,
55+
});
56+
})
57+
.catch((e) => console.error(e))
58+
);
59+
});
60+
61+
} else {
62+
(() => {
63+
const reloadedBySelf = window.sessionStorage.getItem("coiReloadedBySelf");
64+
window.sessionStorage.removeItem("coiReloadedBySelf");
65+
const coepDegrading = (reloadedBySelf == "coepdegrade");
66+
67+
// You can customize the behavior of this script through a global `coi` variable.
68+
const coi = {
69+
shouldRegister: () => !reloadedBySelf,
70+
shouldDeregister: () => false,
71+
coepCredentialless: () => true,
72+
coepDegrade: () => true,
73+
doReload: () => window.location.reload(),
74+
quiet: false,
75+
...window.coi
76+
};
77+
78+
const n = navigator;
79+
const controlling = n.serviceWorker && n.serviceWorker.controller;
80+
81+
// Record the failure if the page is served by serviceWorker.
82+
if (controlling && !window.crossOriginIsolated) {
83+
window.sessionStorage.setItem("coiCoepHasFailed", "true");
84+
}
85+
const coepHasFailed = window.sessionStorage.getItem("coiCoepHasFailed");
86+
87+
if (controlling) {
88+
// Reload only on the first failure.
89+
const reloadToDegrade = coi.coepDegrade() && !(
90+
coepDegrading || window.crossOriginIsolated
91+
);
92+
n.serviceWorker.controller.postMessage({
93+
type: "coepCredentialless",
94+
value: (reloadToDegrade || coepHasFailed && coi.coepDegrade())
95+
? false
96+
: coi.coepCredentialless(),
97+
});
98+
if (reloadToDegrade) {
99+
!coi.quiet && console.log("Reloading page to degrade COEP.");
100+
window.sessionStorage.setItem("coiReloadedBySelf", "coepdegrade");
101+
coi.doReload("coepdegrade");
102+
}
103+
104+
if (coi.shouldDeregister()) {
105+
n.serviceWorker.controller.postMessage({ type: "deregister" });
106+
}
107+
}
108+
109+
// If we're already coi: do nothing. Perhaps it's due to this script doing its job, or COOP/COEP are
110+
// already set from the origin server. Also if the browser has no notion of crossOriginIsolated, just give up here.
111+
if (window.crossOriginIsolated !== false || !coi.shouldRegister()) return;
112+
113+
if (!window.isSecureContext) {
114+
!coi.quiet && console.log("COOP/COEP Service Worker not registered, a secure context is required.");
115+
return;
116+
}
117+
118+
// In some environments (e.g. Firefox private mode) this won't be available
119+
if (!n.serviceWorker) {
120+
!coi.quiet && console.error("COOP/COEP Service Worker not registered, perhaps due to private mode.");
121+
return;
122+
}
123+
124+
n.serviceWorker.register(window.document.currentScript.src).then(
125+
(registration) => {
126+
!coi.quiet && console.log("COOP/COEP Service Worker registered", registration.scope);
127+
128+
registration.addEventListener("updatefound", () => {
129+
!coi.quiet && console.log("Reloading page to make use of updated COOP/COEP Service Worker.");
130+
window.sessionStorage.setItem("coiReloadedBySelf", "updatefound");
131+
coi.doReload();
132+
});
133+
134+
// If the registration is active, but it's not controlling the page
135+
if (registration.active && !n.serviceWorker.controller) {
136+
!coi.quiet && console.log("Reloading page to make use of COOP/COEP Service Worker.");
137+
window.sessionStorage.setItem("coiReloadedBySelf", "notcontrolling");
138+
coi.doReload();
139+
}
140+
},
141+
(err) => {
142+
!coi.quiet && console.error("COOP/COEP Service Worker failed to register:", err);
143+
}
144+
);
145+
})();
146+
}

examples/command.wasm/index-tmpl.html

+6-5
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
overflow-x: scroll;
2525
}
2626
</style>
27+
<script src="../coi-serviceworker.js"></script>
28+
<link rel="icon" href="data:,">
2729
</head>
2830
<body>
2931
<div id="main-container">
@@ -36,11 +38,10 @@
3638
<br><br>
3739

3840
<b>More examples:</b>
39-
<a href="https://whisper.ggerganov.com/">main</a> |
40-
<a href="https://whisper.ggerganov.com/bench">bench</a> |
41-
<a href="https://whisper.ggerganov.com/stream">stream</a> |
42-
<a href="https://whisper.ggerganov.com/command">command</a> |
43-
<a href="https://whisper.ggerganov.com/talk">talk</a> |
41+
<a href="../">main</a> |
42+
<a href="../bench.wasm/">bench</a> |
43+
<a href="../stream.wasm">stream</a> |
44+
<a href="../command.wasm/">command</a> |
4445

4546
<br><br>
4647

examples/stream.wasm/index-tmpl.html

+6-5
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
overflow-x: scroll;
2525
}
2626
</style>
27+
<script src="../coi-serviceworker.js"></script>
28+
<link rel="icon" href="data:,">
2729
</head>
2830
<body>
2931
<div id="main-container">
@@ -36,11 +38,10 @@
3638
<br><br>
3739

3840
<b>More examples:</b>
39-
<a href="https://whisper.ggerganov.com/">main</a> |
40-
<a href="https://whisper.ggerganov.com/bench">bench</a> |
41-
<a href="https://whisper.ggerganov.com/stream">stream</a> |
42-
<a href="https://whisper.ggerganov.com/command">command</a> |
43-
<a href="https://whisper.ggerganov.com/talk">talk</a> |
41+
<a href="../">main</a> |
42+
<a href="../bench.wasm/">bench</a> |
43+
<a href="../stream.wasm">stream</a> |
44+
<a href="../command.wasm/">command</a> |
4445

4546
<br><br>
4647

examples/whisper.wasm/index-tmpl.html

+5-5
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
overflow-x: scroll;
2525
}
2626
</style>
27+
<script src="coi-serviceworker.js"></script>
28+
<link rel="icon" href="data:,">
2729
</head>
2830
<body>
2931
<div id="main-container">
@@ -47,11 +49,9 @@
4749
</ul>
4850

4951
<b>More examples:</b>
50-
<a href="https://whisper.ggerganov.com/">main</a> |
51-
<a href="https://whisper.ggerganov.com/bench">bench</a> |
52-
<a href="https://whisper.ggerganov.com/stream">stream</a> |
53-
<a href="https://whisper.ggerganov.com/command">command</a> |
54-
<a href="https://whisper.ggerganov.com/talk">talk</a> |
52+
<a href="bench.wasm/">bench</a> |
53+
<a href="stream.wasm">stream</a> |
54+
<a href="command.wasm/">command</a> |
5555

5656
<hr>
5757

0 commit comments

Comments
 (0)