danbev commited on
Commit
7b99476
·
unverified ·
1 Parent(s): cbeea6f

ci : add github pages workflow for wasm examples (#2969)

Browse files

* 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: https://github.com/ggerganov/whisper.cpp/issues/2784

.github/workflows/examples-wasm.yml ADDED
@@ -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 CHANGED
@@ -24,6 +24,8 @@
24
  overflow-x: scroll;
25
  }
26
  </style>
 
 
27
  </head>
28
  <body>
29
  <div id="main-container">
@@ -36,11 +38,10 @@
36
  <br><br>
37
 
38
  <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> |
44
 
45
  <br><br>
46
 
 
24
  overflow-x: scroll;
25
  }
26
  </style>
27
+ <script src="../coi-serviceworker.js"></script>
28
+ <link rel="icon" href="data:,">
29
  </head>
30
  <body>
31
  <div id="main-container">
 
38
  <br><br>
39
 
40
  <b>More examples:</b>
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> |
 
45
 
46
  <br><br>
47
 
examples/coi-serviceworker.js ADDED
@@ -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 CHANGED
@@ -24,6 +24,8 @@
24
  overflow-x: scroll;
25
  }
26
  </style>
 
 
27
  </head>
28
  <body>
29
  <div id="main-container">
@@ -36,11 +38,10 @@
36
  <br><br>
37
 
38
  <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> |
44
 
45
  <br><br>
46
 
 
24
  overflow-x: scroll;
25
  }
26
  </style>
27
+ <script src="../coi-serviceworker.js"></script>
28
+ <link rel="icon" href="data:,">
29
  </head>
30
  <body>
31
  <div id="main-container">
 
38
  <br><br>
39
 
40
  <b>More examples:</b>
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> |
 
45
 
46
  <br><br>
47
 
examples/stream.wasm/index-tmpl.html CHANGED
@@ -24,6 +24,8 @@
24
  overflow-x: scroll;
25
  }
26
  </style>
 
 
27
  </head>
28
  <body>
29
  <div id="main-container">
@@ -36,11 +38,10 @@
36
  <br><br>
37
 
38
  <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> |
44
 
45
  <br><br>
46
 
 
24
  overflow-x: scroll;
25
  }
26
  </style>
27
+ <script src="../coi-serviceworker.js"></script>
28
+ <link rel="icon" href="data:,">
29
  </head>
30
  <body>
31
  <div id="main-container">
 
38
  <br><br>
39
 
40
  <b>More examples:</b>
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> |
 
45
 
46
  <br><br>
47
 
examples/whisper.wasm/index-tmpl.html CHANGED
@@ -24,6 +24,8 @@
24
  overflow-x: scroll;
25
  }
26
  </style>
 
 
27
  </head>
28
  <body>
29
  <div id="main-container">
@@ -47,11 +49,9 @@
47
  </ul>
48
 
49
  <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> |
55
 
56
  <hr>
57
 
 
24
  overflow-x: scroll;
25
  }
26
  </style>
27
+ <script src="coi-serviceworker.js"></script>
28
+ <link rel="icon" href="data:,">
29
  </head>
30
  <body>
31
  <div id="main-container">
 
49
  </ul>
50
 
51
  <b>More examples:</b>
52
+ <a href="bench.wasm/">bench</a> |
53
+ <a href="stream.wasm">stream</a> |
54
+ <a href="command.wasm/">command</a> |
 
 
55
 
56
  <hr>
57