Skip to content

Commit 0782ee3

Browse files
Upload PR dev-playground bundles (#8447)
* Upload PR dev-playground bundles * Add safe dev playground PR bundle previews * Post a comment with the playground URL
1 parent 912e189 commit 0782ee3

8 files changed

Lines changed: 179 additions & 18 deletions

File tree

.github/workflows/ci.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,20 @@ jobs:
382382
if: matrix.build_playground
383383
run: yarn workspace playground test
384384

385+
- name: Stage PR dev playground compiler bundle
386+
if: ${{ matrix.build_playground && github.event_name == 'pull_request' && github.event.pull_request.base.ref == 'master' && github.event.pull_request.head.repo.full_name == github.repository }}
387+
env:
388+
PLAYGROUND_PREVIEW_ID: pr-${{ github.event.pull_request.number }}
389+
run: yarn workspace dev-playground stage-local-bundle "$PLAYGROUND_PREVIEW_ID"
390+
391+
- name: "Upload artifacts: PR dev playground compiler bundle"
392+
if: ${{ matrix.build_playground && github.event_name == 'pull_request' && github.event.pull_request.base.ref == 'master' && github.event.pull_request.head.repo.full_name == github.repository }}
393+
uses: actions/upload-artifact@v7
394+
with:
395+
name: dev-playground-pr-${{ github.event.pull_request.number }}-bundle
396+
path: packages/dev-playground/public/playground-bundles/pr-${{ github.event.pull_request.number }}
397+
if-no-files-found: error
398+
385399
- name: Stage dev playground compiler bundle
386400
if: ${{ matrix.build_playground && github.event_name == 'push' && github.ref == 'refs/heads/master' }}
387401
run: yarn workspace dev-playground stage-master-bundle
@@ -457,6 +471,7 @@ jobs:
457471
env:
458472
VITE_DEFAULT_COMPILER_VERSION: master
459473
VITE_COMPILER_VERSIONS: '[{"id":"master","label":"master"}]'
474+
VITE_COMPILER_PREVIEW_ROOT: https://cdn.rescript-lang.org/dev-playground-bundles
460475
GITHUB_PAGES_PATH: dev-playground
461476
PLAYGROUND_BUNDLE_ID: master
462477
steps:
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
name: Playground Preview Cleanup
2+
3+
on:
4+
pull_request_target:
5+
branches: [master]
6+
types: [closed]
7+
8+
jobs:
9+
cleanup:
10+
if: ${{ github.event.pull_request.head.repo.full_name == github.repository }}
11+
runs-on: ubuntu-24.04
12+
steps:
13+
- name: Setup Rclone
14+
uses: cometkim/rclone-actions/setup-rclone@main
15+
16+
- name: Configure Rclone remote
17+
uses: cometkim/rclone-actions/configure-remote/s3-provider@main
18+
with:
19+
name: rescript
20+
provider: Cloudflare
21+
endpoint: https://${{ vars.CLOUDFLARE_ACCOUNT_ID }}.r2.cloudflarestorage.com
22+
access-key-id: ${{ secrets.CLOUDFLARE_R2_ACCESS_KEY_ID }}
23+
secret-access-key: ${{ secrets.CLOUDFLARE_R2_SECRET_ACCESS_KEY }}
24+
acl: private
25+
26+
- name: Delete preview bundle
27+
continue-on-error: true
28+
run: rclone purge "rescript:cdn-assets/dev-playground-bundles/pr-${{ github.event.pull_request.number }}"
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
name: Playground Preview Upload
2+
3+
on:
4+
workflow_run:
5+
workflows: [CI]
6+
types: [completed]
7+
8+
jobs:
9+
upload:
10+
if: ${{ github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.head_repository.full_name == github.repository && github.event.workflow_run.pull_requests[0].base.ref == 'master' }}
11+
runs-on: ubuntu-24.04
12+
permissions:
13+
actions: read
14+
contents: read
15+
pull-requests: write
16+
env:
17+
PLAYGROUND_PREVIEW_ID: pr-${{ github.event.workflow_run.pull_requests[0].number }}
18+
steps:
19+
- name: Download preview bundle artifact
20+
uses: actions/download-artifact@v8
21+
with:
22+
name: dev-playground-pr-${{ github.event.workflow_run.pull_requests[0].number }}-bundle
23+
path: dev-playground-preview-bundle
24+
run-id: ${{ github.event.workflow_run.id }}
25+
github-token: ${{ secrets.GITHUB_TOKEN }}
26+
27+
- name: Setup Rclone
28+
uses: cometkim/rclone-actions/setup-rclone@main
29+
30+
- name: Configure Rclone remote
31+
uses: cometkim/rclone-actions/configure-remote/s3-provider@main
32+
with:
33+
name: rescript
34+
provider: Cloudflare
35+
endpoint: https://${{ vars.CLOUDFLARE_ACCOUNT_ID }}.r2.cloudflarestorage.com
36+
access-key-id: ${{ secrets.CLOUDFLARE_R2_ACCESS_KEY_ID }}
37+
secret-access-key: ${{ secrets.CLOUDFLARE_R2_SECRET_ACCESS_KEY }}
38+
acl: private
39+
40+
- name: Upload preview bundle
41+
run: |
42+
rclone sync \
43+
--stats 5 \
44+
--checkers 5000 \
45+
--transfers 8 \
46+
--buffer-size 128M \
47+
--s3-no-check-bucket \
48+
--s3-chunk-size 128M \
49+
--s3-upload-concurrency 8 \
50+
--fast-list \
51+
"dev-playground-preview-bundle" \
52+
"rescript:cdn-assets/dev-playground-bundles/${PLAYGROUND_PREVIEW_ID}/bundle"
53+
54+
- name: Comment playground preview URL
55+
uses: thollander/actions-comment-pull-request@v3
56+
with:
57+
pr-number: ${{ github.event.workflow_run.pull_requests[0].number }}
58+
comment-tag: dev-playground-preview
59+
message: |
60+
Developer playground preview: https://${{ github.repository_owner }}.github.io/${{ github.event.repository.name }}/dev-playground/?version=pr-${{ github.event.workflow_run.pull_requests[0].number }}

packages/dev-playground/src/Bindings.res

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ module Env = {
1010
external viteDefaultCompilerVersion: option<string> =
1111
"import.meta.env.VITE_DEFAULT_COMPILER_VERSION"
1212
@val external viteCompilerVersions: option<string> = "import.meta.env.VITE_COMPILER_VERSIONS"
13+
@val
14+
external viteCompilerPreviewRoot: option<string> = "import.meta.env.VITE_COMPILER_PREVIEW_ROOT"
1315
@val external viteBaseUrl: option<string> = "import.meta.env.BASE_URL"
1416
}
1517

packages/dev-playground/src/CompilerApi.res

Lines changed: 65 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
type compilerVersion = {
2+
versionId: string,
3+
versionLabel: string,
4+
versionRoot: option<string>,
5+
}
6+
17
module Version = {
28
type t = {
39
id: string,
@@ -10,12 +16,28 @@ module Version = {
1016
| _ => None
1117
}
1218

19+
let normalizeRoot = root =>
20+
if root->String.endsWith("/") {
21+
root->String.slice(~start=0, ~end=root->String.length - 1)
22+
} else {
23+
root
24+
}
25+
26+
let toPublic = (version: compilerVersion): t => {
27+
id: version.versionId,
28+
label: version.versionLabel,
29+
}
30+
1331
let fromJson = json =>
1432
switch json {
1533
| JSON.Object(item) =>
1634
let? Some(id) = item->jsonStringField("id")
1735
let? Some(label) = item->jsonStringField("label")
18-
Some({id, label})
36+
Some({
37+
versionId: id,
38+
versionLabel: label,
39+
versionRoot: item->jsonStringField("root")->Option.map(normalizeRoot),
40+
})
1941
| _ => None
2042
}
2143
}
@@ -86,7 +108,7 @@ let pathFromBase = relativePath => {
86108
}
87109

88110
let parseCompilerVersions = defaultVersion => {
89-
let fallback = [{Version.id: defaultVersion, label: defaultVersion}]
111+
let fallback = [{versionId: defaultVersion, versionLabel: defaultVersion, versionRoot: None}]
90112
switch Env.viteCompilerVersions {
91113
| None | Some("") => fallback
92114
| Some(versionJson) =>
@@ -100,8 +122,13 @@ let parseCompilerVersions = defaultVersion => {
100122
}
101123
}
102124

103-
let availableCompilerVersions = parseCompilerVersions(defaultConfig.compilerVersion)
125+
let compilerVersions = parseCompilerVersions(defaultConfig.compilerVersion)
126+
let availableCompilerVersions = compilerVersions->Array.map(Version.toPublic)
104127
let compilerRoot = pathFromBase("playground-bundles")
128+
let compilerPreviewRoot = switch Env.viteCompilerPreviewRoot {
129+
| Some(root) => root === "" ? None : Some(root->Version.normalizeRoot)
130+
| None => None
131+
}
105132
let loadedScripts: Map.t<string, promise<unit>> = Map.make()
106133
let compilerApis: Map.t<string, compilerApi> = Map.make()
107134
let compilers: Map.t<string, compilerInstance> = Map.make()
@@ -116,6 +143,41 @@ let hasFunction = (value, name) =>
116143

117144
let versionOrDefault = version => version === "" ? defaultConfig.compilerVersion : version
118145

146+
let isPreviewVersion = version => version->String.search(/^pr-[0-9]+$/) === 0
147+
148+
let previewVersionRoot = version =>
149+
switch compilerPreviewRoot {
150+
| Some(root) if version->isPreviewVersion => Some(`${root}/${version}/bundle`)
151+
| _ => None
152+
}
153+
154+
let versionRoot = version => {
155+
let selectedVersion = versionOrDefault(version)
156+
switch compilerVersions->Array.findMap(version =>
157+
version.versionId === selectedVersion ? version.versionRoot : None
158+
) {
159+
| Some(root) => root
160+
| None =>
161+
switch selectedVersion->previewVersionRoot {
162+
| Some(root) => root
163+
| None => `${compilerRoot}/${selectedVersion}`
164+
}
165+
}
166+
}
167+
168+
let isConfiguredVersion = version =>
169+
compilerVersions->Array.some(compilerVersion => compilerVersion.versionId === version)
170+
171+
let isLoadableVersion = version =>
172+
version->isConfiguredVersion || version->previewVersionRoot->Option.isSome
173+
174+
let selectableCompilerVersions = activeVersion =>
175+
if activeVersion->isConfiguredVersion || !(activeVersion->previewVersionRoot->Option.isSome) {
176+
availableCompilerVersions
177+
} else {
178+
Array.concat(availableCompilerVersions, [{Version.id: activeVersion, label: activeVersion}])
179+
}
180+
119181
let createScriptLoadPromise = src =>
120182
Promise.make((resolve, reject) => {
121183
let document = Document.current
@@ -140,8 +202,6 @@ let loadScript = (src, ~cache=true) =>
140202
createScriptLoadPromise(src)
141203
}
142204

143-
let versionRoot = version => `${compilerRoot}/${versionOrDefault(version)}`
144-
145205
let applyConfig = (
146206
instance,
147207
~moduleSystem: PlaygroundConfig.moduleSystem,

packages/dev-playground/src/CompilerApi.resi

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,9 @@ type formatResult = result<string, failure>
3838

3939
let defaultConfig: PlaygroundConfig.t
4040

41-
let availableCompilerVersions: array<Version.t>
41+
let selectableCompilerVersions: string => array<Version.t>
42+
43+
let isLoadableVersion: string => bool
4244

4345
let init: string => promise<info>
4446

packages/dev-playground/src/Main.res

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -231,9 +231,9 @@ module SettingsPanel = {
231231
}}
232232
>
233233
{Node.fragment(
234-
CompilerApi.availableCompilerVersions->Array.map(version =>
235-
<option value=version.id> {Node.text(version.label)} </option>
236-
),
234+
CompilerApi.selectableCompilerVersions(
235+
Signal.get(config).compilerVersion,
236+
)->Array.map(version => <option value=version.id> {Node.text(version.label)} </option>),
237237
)}
238238
</select>
239239
</section>

packages/dev-playground/src/UrlState.res

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -85,13 +85,10 @@ let queryExperimentalFeatures = defaultExperimentalFeatures =>
8585
| _ => defaultExperimentalFeatures
8686
}
8787

88-
let queryConfig = (
89-
~defaultConfig: PlaygroundConfig.t,
90-
~availableCompilerVersions: array<CompilerApi.Version.t>,
91-
) => {
88+
let queryConfig = (~defaultConfig: PlaygroundConfig.t) => {
9289
let requestedCompilerVersion = queryCompilerVersion(defaultConfig.compilerVersion)
9390
let compilerVersion =
94-
availableCompilerVersions->Array.some(version => version.id === requestedCompilerVersion)
91+
requestedCompilerVersion->CompilerApi.isLoadableVersion
9592
? requestedCompilerVersion
9693
: defaultConfig.compilerVersion
9794

@@ -106,10 +103,7 @@ let queryConfig = (
106103

107104
let init = async (~defaultSource): state => {
108105
let source = await initialSource(defaultSource)
109-
let config = queryConfig(
110-
~defaultConfig=CompilerApi.defaultConfig,
111-
~availableCompilerVersions=CompilerApi.availableCompilerVersions,
112-
)
106+
let config = queryConfig(~defaultConfig=CompilerApi.defaultConfig)
113107
{source, config}
114108
}
115109

0 commit comments

Comments
 (0)