-
Notifications
You must be signed in to change notification settings - Fork 101
feat: add branch-specific PR preview environments #87
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
sudip-mondal-2002
merged 5 commits into
sudip-mondal-2002:main
from
ionfwsrijan:feature/pr-preview-pages
May 16, 2026
Merged
Changes from 2 commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
115e347
Add branch-specific PR preview workflow
ionfwsrijan fe856b1
Address preview workflow review feedback
ionfwsrijan c5fd276
Reuse CI web build for PR previews
ionfwsrijan 42f72aa
Harden CI dependency setup for previews
ionfwsrijan 2292998
Merge branch 'main' into feature/pr-preview-pages
sudip-mondal-2002 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,129 @@ | ||
| name: Build Preview | ||
|
|
||
| on: | ||
| pull_request: | ||
| branches: | ||
| - main | ||
| - develop | ||
| types: | ||
| - opened | ||
| - synchronize | ||
| - reopened | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| concurrency: | ||
| group: build-preview-${{ github.event.pull_request.number }} | ||
| cancel-in-progress: true | ||
|
|
||
| jobs: | ||
| build-preview: | ||
| name: Build Web Preview | ||
| runs-on: ubuntu-latest | ||
| env: | ||
| EMSDK_VERSION: 3.1.51 | ||
| steps: | ||
| - name: Checkout PR commit | ||
| uses: actions/checkout@v6 | ||
| with: | ||
| ref: ${{ github.event.pull_request.head.sha }} | ||
| fetch-depth: 0 | ||
| persist-credentials: false | ||
|
|
||
| - name: Cache Emscripten SDK & ports | ||
| uses: actions/cache@v5 | ||
| id: emsdk-cache | ||
| with: | ||
| path: | | ||
| emsdk | ||
| ~/.emscripten_cache | ||
| key: emsdk-${{ env.EMSDK_VERSION }}-${{ runner.os }}-${{ hashFiles('CMakeLists.txt') }} | ||
| restore-keys: | | ||
| emsdk-${{ env.EMSDK_VERSION }}-${{ runner.os }}- | ||
|
|
||
| - name: Setup Emscripten | ||
| if: steps.emsdk-cache.outputs.cache-hit != 'true' | ||
| run: | | ||
| if [ -d emsdk ]; then | ||
| cd emsdk && git pull | ||
| else | ||
| git clone https://github.com/emscripten-core/emsdk.git | ||
| cd emsdk | ||
| fi | ||
| ./emsdk install $EMSDK_VERSION | ||
| ./emsdk activate $EMSDK_VERSION | ||
|
|
||
| - name: Activate Emscripten | ||
| run: | | ||
| cd emsdk | ||
| ./emsdk activate $EMSDK_VERSION | ||
| echo "${{ github.workspace }}/emsdk" >> "$GITHUB_PATH" | ||
| echo "${{ github.workspace }}/emsdk/upstream/emscripten" >> "$GITHUB_PATH" | ||
|
|
||
| - name: Setup External Dependencies | ||
| run: | | ||
| git clone --depth 1 --branch v1.90.1 https://github.com/ocornut/imgui.git external/imgui & | ||
| curl -sL -o external/nanosvg.h https://raw.githubusercontent.com/memononen/nanosvg/master/src/nanosvg.h & | ||
| curl -sL -o external/nanosvgrast.h https://raw.githubusercontent.com/memononen/nanosvg/master/src/nanosvgrast.h & | ||
| curl -sL -o external/dr_wav.h https://raw.githubusercontent.com/mackron/dr_libs/master/dr_wav.h & | ||
| mkdir -p external/kiss_fft | ||
| curl -sL -o external/kiss_fft/kiss_fft.h https://raw.githubusercontent.com/mborgerding/kissfft/master/kiss_fft.h & | ||
| curl -sL -o external/kiss_fft/kiss_fft.c https://raw.githubusercontent.com/mborgerding/kissfft/master/kiss_fft.c & | ||
| curl -sL -o external/kiss_fft/_kiss_fft_guts.h https://raw.githubusercontent.com/mborgerding/kissfft/master/_kiss_fft_guts.h & | ||
| curl -sL -o external/kiss_fft/kiss_fft_log.h https://raw.githubusercontent.com/mborgerding/kissfft/master/kiss_fft_log.h & | ||
| wait | ||
|
|
||
| - name: Generate Preview Version | ||
| id: version | ||
| run: | | ||
| COUNT=$(git rev-list --count HEAD) | ||
| echo "version=0.1.${COUNT}-pr.${{ github.event.pull_request.number }}.${{ github.run_number }}" >> "$GITHUB_OUTPUT" | ||
|
|
||
| - name: Build WASM | ||
| run: | | ||
| source emsdk/emsdk_env.sh | ||
| mkdir -p build-web && cd build-web | ||
| emcmake cmake -DCMAKE_BUILD_TYPE=Release -DAMPLITRON_VERSION="${{ steps.version.outputs.version }}" .. | ||
| emmake make -j"$(nproc)" | ||
|
|
||
| - name: Stage preview artifact | ||
| run: | | ||
| mkdir -p preview .preview-meta | ||
|
|
||
| for file in \ | ||
| build-web/index.html \ | ||
| build-web/index.js \ | ||
| build-web/index.wasm \ | ||
| build-web/index.data \ | ||
| build-web/*.worker.js \ | ||
| build-web/coi-serviceworker.js | ||
| do | ||
| [ -e "$file" ] && cp "$file" preview/ | ||
| done | ||
|
|
||
| test -f preview/index.html | ||
| test -f preview/index.js | ||
| test -f preview/index.wasm | ||
| touch preview/.nojekyll | ||
|
|
||
| cat > .preview-meta/preview.env <<EOF | ||
| PR_NUMBER=${{ github.event.pull_request.number }} | ||
| HEAD_SHA=${{ github.event.pull_request.head.sha }} | ||
| PREVIEW_PATH=pr-previews/pr-${{ github.event.pull_request.number }} | ||
| PREVIEW_URL=https://amplitron.sudipmondal.co.in/pr-previews/pr-${{ github.event.pull_request.number }}/ | ||
| EOF | ||
|
|
||
| - name: Upload preview artifact | ||
| uses: actions/upload-artifact@v7 | ||
| with: | ||
| name: pr-preview | ||
| path: preview/ | ||
| retention-days: 3 | ||
|
|
||
| - name: Upload preview metadata | ||
| uses: actions/upload-artifact@v7 | ||
| with: | ||
| name: pr-preview-metadata | ||
| path: .preview-meta/ | ||
| retention-days: 3 | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,199 @@ | ||
| name: Deploy Preview | ||
|
|
||
| on: | ||
| workflow_run: | ||
| workflows: | ||
| - Build Preview | ||
| types: | ||
| - completed | ||
| pull_request_target: | ||
| types: | ||
| - closed | ||
|
|
||
| permissions: | ||
| contents: write | ||
| pull-requests: write | ||
|
|
||
|
coderabbitai[bot] marked this conversation as resolved.
coderabbitai[bot] marked this conversation as resolved.
|
||
| concurrency: | ||
| group: deploy-preview-${{ github.event.workflow_run.id || github.event.pull_request.number }} | ||
| cancel-in-progress: false | ||
|
|
||
| jobs: | ||
| deploy-preview: | ||
| name: Deploy PR Preview | ||
| if: > | ||
| github.event_name == 'workflow_run' && | ||
| github.event.workflow_run.event == 'pull_request' && | ||
| github.event.workflow_run.conclusion == 'success' | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Download preview artifact | ||
| uses: actions/download-artifact@v8 | ||
| with: | ||
| name: pr-preview | ||
| path: preview | ||
| run-id: ${{ github.event.workflow_run.id }} | ||
| github-token: ${{ secrets.GITHUB_TOKEN }} | ||
|
|
||
| - name: Download preview metadata | ||
| uses: actions/download-artifact@v8 | ||
| with: | ||
| name: pr-preview-metadata | ||
| path: .preview-meta | ||
| run-id: ${{ github.event.workflow_run.id }} | ||
| github-token: ${{ secrets.GITHUB_TOKEN }} | ||
|
|
||
| - name: Load preview metadata | ||
| id: preview-meta | ||
| run: | | ||
| set -a | ||
| . .preview-meta/preview.env | ||
| set +a | ||
|
|
||
| echo "pr_number=$PR_NUMBER" >> "$GITHUB_OUTPUT" | ||
| echo "head_sha=$HEAD_SHA" >> "$GITHUB_OUTPUT" | ||
| echo "preview_path=$PREVIEW_PATH" >> "$GITHUB_OUTPUT" | ||
| echo "preview_url=$PREVIEW_URL" >> "$GITHUB_OUTPUT" | ||
| echo "short_sha=${HEAD_SHA:0:7}" >> "$GITHUB_OUTPUT" | ||
|
|
||
| - name: Check pull request state | ||
| id: pr-state | ||
| uses: actions/github-script@v8 | ||
| with: | ||
| script: | | ||
| const { owner, repo } = context.repo; | ||
| const pull_number = Number('${{ steps.preview-meta.outputs.pr_number }}'); | ||
| const { data: pull } = await github.rest.pulls.get({ | ||
| owner, | ||
| repo, | ||
| pull_number, | ||
| }); | ||
| core.setOutput('state', pull.state); | ||
|
|
||
| - name: Publish preview to GitHub Pages | ||
| if: steps.pr-state.outputs.state == 'open' | ||
| uses: peaceiris/actions-gh-pages@v4 | ||
| with: | ||
| github_token: ${{ secrets.GITHUB_TOKEN }} | ||
| publish_dir: ./preview | ||
| publish_branch: gh-pages | ||
| destination_dir: ${{ steps.preview-meta.outputs.preview_path }} | ||
| keep_files: true | ||
| enable_jekyll: false | ||
|
|
||
| - name: Comment preview URL on PR | ||
| if: steps.pr-state.outputs.state == 'open' | ||
| uses: actions/github-script@v8 | ||
| env: | ||
| PR_NUMBER: ${{ steps.preview-meta.outputs.pr_number }} | ||
| PREVIEW_URL: ${{ steps.preview-meta.outputs.preview_url }} | ||
| PREVIEW_PATH: ${{ steps.preview-meta.outputs.preview_path }} | ||
| SHORT_SHA: ${{ steps.preview-meta.outputs.short_sha }} | ||
| with: | ||
| script: | | ||
| const marker = '<!-- amplitron-pr-preview -->'; | ||
| const { owner, repo } = context.repo; | ||
| const issue_number = Number(process.env.PR_NUMBER); | ||
| const body = [ | ||
| marker, | ||
| '### PR Preview Ready', | ||
| '', | ||
| `Preview URL: ${process.env.PREVIEW_URL}`, | ||
| '', | ||
| `Built from commit \`${process.env.SHORT_SHA}\` and deployed to \`${process.env.PREVIEW_PATH}\`.`, | ||
| 'This preview updates automatically when the PR branch changes and is removed when the PR closes.', | ||
| ].join('\n'); | ||
|
|
||
| const { data: comments } = await github.rest.issues.listComments({ | ||
| owner, | ||
| repo, | ||
| issue_number, | ||
| per_page: 100, | ||
| }); | ||
| const previous = comments.find((comment) => | ||
| comment.user?.type === 'Bot' && comment.body?.includes(marker) | ||
| ); | ||
|
|
||
| if (previous) { | ||
| await github.rest.issues.updateComment({ | ||
| owner, | ||
| repo, | ||
| comment_id: previous.id, | ||
| body, | ||
| }); | ||
| } else { | ||
| await github.rest.issues.createComment({ | ||
| owner, | ||
| repo, | ||
| issue_number, | ||
| body, | ||
| }); | ||
| } | ||
|
|
||
| cleanup-preview: | ||
| name: Remove Closed PR Preview | ||
| if: github.event_name == 'pull_request_target' | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Checkout GitHub Pages branch | ||
| id: pages-checkout | ||
| uses: actions/checkout@v6 | ||
| continue-on-error: true | ||
| with: | ||
| ref: gh-pages | ||
| path: pages | ||
| token: ${{ secrets.GITHUB_TOKEN }} | ||
|
|
||
| - name: Remove preview directory | ||
| if: steps.pages-checkout.outcome == 'success' | ||
| working-directory: pages | ||
| env: | ||
| PR_NUMBER: ${{ github.event.pull_request.number }} | ||
| run: | | ||
| target="pr-previews/pr-${PR_NUMBER}" | ||
|
|
||
| if [ ! -d "$target" ]; then | ||
| echo "No preview directory found at $target" | ||
| exit 0 | ||
| fi | ||
|
|
||
| rm -rf "$target" | ||
| git config user.name "github-actions[bot]" | ||
| git config user.email "41898282+github-actions[bot]@users.noreply.github.com" | ||
| git add -A "$target" | ||
| git commit -m "Remove preview for PR #${PR_NUMBER}" | ||
| git push | ||
|
|
||
| - name: Comment cleanup on PR | ||
| if: steps.pages-checkout.outcome == 'success' | ||
| uses: actions/github-script@v8 | ||
| with: | ||
| script: | | ||
| const marker = '<!-- amplitron-pr-preview -->'; | ||
| const { owner, repo } = context.repo; | ||
| const issue_number = context.payload.pull_request.number; | ||
| const body = [ | ||
| marker, | ||
| '### PR Preview Removed', | ||
| '', | ||
| 'The GitHub Pages preview for this PR has been removed because the PR was closed.', | ||
| ].join('\n'); | ||
|
|
||
| const { data: comments } = await github.rest.issues.listComments({ | ||
| owner, | ||
| repo, | ||
| issue_number, | ||
| per_page: 100, | ||
| }); | ||
| const previous = comments.find((comment) => | ||
| comment.user?.type === 'Bot' && comment.body?.includes(marker) | ||
| ); | ||
|
|
||
| if (previous) { | ||
| await github.rest.issues.updateComment({ | ||
| owner, | ||
| repo, | ||
| comment_id: previous.id, | ||
| body, | ||
| }); | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -39,6 +39,25 @@ This document explains the CI/CD pipeline and how to create releases. | |
| 4. Uploads installers to the release | ||
| 5. Deploys web demo and download page to GitHub Pages | ||
|
|
||
| ### 3. PR Preview Workflows (`.github/workflows/build-preview.yml`, `.github/workflows/deploy-preview.yml`) | ||
|
|
||
| **Triggers**: Pull Requests to `main` or `develop`, and completed PR preview builds | ||
|
|
||
| **What they do**: | ||
| 1. Build the Emscripten web target for each open Pull Request | ||
| 2. Upload only the static preview files (`index.html`, JavaScript, WebAssembly, data, worker, and service worker files) | ||
| 3. Deploy the preview to GitHub Pages under `pr-previews/pr-<number>/` | ||
| 4. Post or update a PR comment with the live preview URL | ||
| 5. Remove the preview directory automatically when the PR is closed | ||
|
|
||
| Preview URLs follow this format: | ||
|
|
||
| ```text | ||
| https://amplitron.sudipmondal.co.in/pr-previews/pr-<number>/ | ||
| ``` | ||
|
|
||
|
Comment on lines
+54
to
+58
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please use |
||
| The build and deploy steps are intentionally split across two workflows. Pull Request code is built with read-only permissions, while the Pages deployment workflow only publishes the generated artifact. This keeps preview deployments available for review without giving PR build scripts write access to the repository. | ||
|
|
||
| ## Creating a Release | ||
|
|
||
| ### Step 1: Prepare the Release | ||
|
|
@@ -97,7 +116,7 @@ To trigger a release manually, push to `main` and let CI complete. The release w | |
| 3. Branch: `gh-pages` / `root` | ||
| 4. Click Save | ||
|
|
||
| The website will be available at: https://sudip-mondal-2002.github.io/Amplitron/ | ||
| The website will be available at: https://amplitron.sudipmondal.co.in/ | ||
|
|
||
| ### Update the Download Page | ||
|
|
||
|
|
||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
Repository: sudip-mondal-2002/Amplitron
Length of output: 103
🏁 Script executed:
Repository: sudip-mondal-2002/Amplitron
Length of output: 1609
🌐 Web query:
curl -sL vs -fsSL flags difference error handling HTTP💡 Result:
The primary difference between the curl flag combinations -sL and -fsSL lies in how they handle HTTP errors and error reporting [1][2]. -sL (Silent, Location) - -s (--silent): Disables the progress meter and error messages [3][4]. - -L (--location): Tells curl to follow HTTP 3xx redirects [5][6]. - Error Handling: Because -s is used without -S, curl will suppress error messages if the request fails (e.g., DNS issues, connection refused, or SSL errors) [1]. This can make debugging difficult in scripts, as the command may fail silently without providing feedback [1]. -fsSL (Fail, Silent, Show-error, Location) - -f (--fail): Tells curl to fail silently (no output) on server errors (HTTP 4xx or 5xx) [7][6]. Without this flag, curl treats HTTP error responses as successful transfers and will output the error page content [2]. - -s (--silent): Disables the progress meter [3][4]. - -S (--show-error): When used with -s, this ensures that if curl fails, it will still output the error message to stderr [1][3]. - -L (--location): Follows HTTP 3xx redirects [5][6]. - Error Handling: This combination is generally preferred for scripts [1]. It ensures that HTTP server errors cause the command to exit with a non-zero status (error 22) [7][2], while still providing diagnostic error messages if a network or connection failure occurs [1]. Summary of Key Flags - -f (--fail): Prevents curl from outputting the body of an HTTP error response and forces a non-zero exit code [7][2]. - -s (--silent): Suppresses progress bars and other output [3][4]. - -S (--show-error): Ensures error messages are printed even when -s is active [1][3]. - -L (--location): Enables automatic following of redirects [5][6]. For robust scripting, -fsSL is recommended over -sL because it provides better error visibility and forces the command to fail explicitly on HTTP errors [1][2].
Citations:
Create
external/before starting background operations and use-fsSLfor robust curl error handling.Lines 66-69 are backgrounded before the
external/directory is created on line 70, so this step has a race condition that can fail intermittently with "No such file or directory" ongit cloneandcurloperations. Additionally,curl -sLsilently outputs HTTP error pages (404/500) as file content instead of failing the build. Switch to-fsSLto fail explicitly on upstream errors.Suggested fix
- name: Setup External Dependencies run: | + mkdir -p external external/kiss_fft git clone --depth 1 --branch v1.90.1 https://github.com/ocornut/imgui.git external/imgui & - curl -sL -o external/nanosvg.h https://raw.githubusercontent.com/memononen/nanosvg/master/src/nanosvg.h & - curl -sL -o external/nanosvgrast.h https://raw.githubusercontent.com/memononen/nanosvg/master/src/nanosvgrast.h & - curl -sL -o external/dr_wav.h https://raw.githubusercontent.com/mackron/dr_libs/master/dr_wav.h & - mkdir -p external/kiss_fft - curl -sL -o external/kiss_fft/kiss_fft.h https://raw.githubusercontent.com/mborgerding/kissfft/master/kiss_fft.h & - curl -sL -o external/kiss_fft/kiss_fft.c https://raw.githubusercontent.com/mborgerding/kissfft/master/kiss_fft.c & - curl -sL -o external/kiss_fft/_kiss_fft_guts.h https://raw.githubusercontent.com/mborgerding/kissfft/master/_kiss_fft_guts.h & - curl -sL -o external/kiss_fft/kiss_fft_log.h https://raw.githubusercontent.com/mborgerding/kissfft/master/kiss_fft_log.h & + curl -fsSL -o external/nanosvg.h https://raw.githubusercontent.com/memononen/nanosvg/master/src/nanosvg.h & + curl -fsSL -o external/nanosvgrast.h https://raw.githubusercontent.com/memononen/nanosvg/master/src/nanosvgrast.h & + curl -fsSL -o external/dr_wav.h https://raw.githubusercontent.com/mackron/dr_libs/master/dr_wav.h & + curl -fsSL -o external/kiss_fft/kiss_fft.h https://raw.githubusercontent.com/mborgerding/kissfft/master/kiss_fft.h & + curl -fsSL -o external/kiss_fft/kiss_fft.c https://raw.githubusercontent.com/mborgerding/kissfft/master/kiss_fft.c & + curl -fsSL -o external/kiss_fft/_kiss_fft_guts.h https://raw.githubusercontent.com/mborgerding/kissfft/master/_kiss_fft_guts.h & + curl -fsSL -o external/kiss_fft/kiss_fft_log.h https://raw.githubusercontent.com/mborgerding/kissfft/master/kiss_fft_log.h & wait📝 Committable suggestion
🤖 Prompt for AI Agents