-
Notifications
You must be signed in to change notification settings - Fork 0
feat: EmailTemplateAdmin with EmailBuilder MUI canvas #142
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
base: main
Are you sure you want to change the base?
Changes from all commits
7ee8b1e
13da3da
bd5a474
249019f
57ad928
9df3088
50e6742
9ca3737
a9e728c
fcf4b27
8d0c123
f266571
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,231 @@ | ||||||||||||||||
| name: Visual review | ||||||||||||||||
|
|
||||||||||||||||
| on: | ||||||||||||||||
| pull_request: | ||||||||||||||||
| types: [opened, synchronize, reopened, labeled, unlabeled, closed] | ||||||||||||||||
|
|
||||||||||||||||
| # Per-PR group so label-accept and capture do not cancel each other incorrectly. | ||||||||||||||||
| concurrency: | ||||||||||||||||
| group: ${{ contains(fromJSON('["labeled","unlabeled"]'), github.event.action) && format('visual-label-pr-{0}', github.event.pull_request.number) || format('visual-review-pr-{0}', github.event.pull_request.number) }} | ||||||||||||||||
| cancel-in-progress: true | ||||||||||||||||
|
|
||||||||||||||||
| permissions: | ||||||||||||||||
| contents: write | ||||||||||||||||
| pull-requests: write | ||||||||||||||||
|
Comment on lines
+12
to
+14
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. 🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift Remove write scopes from the PR code execution job. These workflow-level permissions apply while Split capture from deployment and commenting. Keep the capture job read-only. Grant 🧰 Tools🪛 zizmor (1.29.0)[error] 13-13: overly broad permissions (excessive-permissions): contents: write is overly broad at the workflow level (excessive-permissions) [error] 14-14: overly broad permissions (excessive-permissions): pull-requests: write is overly broad at the workflow level (excessive-permissions) 🤖 Prompt for AI AgentsSource: Linters/SAST tools |
||||||||||||||||
|
|
||||||||||||||||
| jobs: | ||||||||||||||||
| # Removes hosted report when the PR closes (InkAds-style pr-preview cleanup). | ||||||||||||||||
| preview-cleanup: | ||||||||||||||||
| name: preview-cleanup | ||||||||||||||||
| if: > | ||||||||||||||||
| github.event.action == 'closed' && | ||||||||||||||||
| github.event.pull_request.head.repo.full_name == github.repository | ||||||||||||||||
| runs-on: ubuntu-latest | ||||||||||||||||
| steps: | ||||||||||||||||
| - uses: actions/checkout@v4 | ||||||||||||||||
| - name: Remove PR visual preview | ||||||||||||||||
| uses: rossjrw/pr-preview-action@v1.8.1 | ||||||||||||||||
|
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. 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win 🔎 Supported by static analysis🏁 Script executed: #!/bin/bash
set -euo pipefail
ref="$(gh api repos/rossjrw/pr-preview-action/git/ref/tags/v1.8.1)"
type="$(jq -r '.object.type' <<<"$ref")"
sha="$(jq -r '.object.sha' <<<"$ref")"
if [ "$type" = "tag" ]; then
sha="$(gh api "repos/rossjrw/pr-preview-action/git/tags/$sha" --jq '.object.sha')"
fi
printf 'Pin rossjrw/pr-preview-action@v1.8.1 to %s\n' "$sha"Repository: singleton-sd/post-kit Length of output: 240 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '%s\n' '--- workflow references and permissions ---'
rg -n -C 3 'rossjrw/pr-preview-action|permissions:|contents:|pull-requests:' .github/workflows/visual-review.yml
printf '%s\n' '--- tag object and dereferenced commit ---'
ref="$(gh api repos/rossjrw/pr-preview-action/git/ref/tags/v1.8.1)"
printf '%s\n' "$ref" | jq '{type: .object.type, sha: .object.sha}'
type="$(jq -r '.object.type' <<<"$ref")"
sha="$(jq -r '.object.sha' <<<"$ref")"
if [ "$type" = "tag" ]; then
sha="$(gh api "repos/rossjrw/pr-preview-action/git/tags/$sha" --jq '.object.sha')"
fi
gh api "repos/rossjrw/pr-preview-action/commits/$sha" --jq '{sha: .sha, message: .commit.message}'
printf 'dereferenced_commit=%s\n' "$sha"Repository: singleton-sd/post-kit Length of output: 1806 🤖 get_repo_knowledge executed:
Length of output: 2679 Pin all The workflow grants 🤖 Prompt for AI Agents |
||||||||||||||||
| with: | ||||||||||||||||
| source-dir: . | ||||||||||||||||
| preview-branch: gh-pages | ||||||||||||||||
| pages-base-url: https://singleton-sd.github.io/post-kit | ||||||||||||||||
| umbrella-dir: pr-preview | ||||||||||||||||
| action: remove | ||||||||||||||||
| comment: false | ||||||||||||||||
|
|
||||||||||||||||
| visual-review: | ||||||||||||||||
| name: visual-review | ||||||||||||||||
| if: > | ||||||||||||||||
| github.event.action != 'closed' && | ||||||||||||||||
| github.event.pull_request.head.repo.full_name == github.repository | ||||||||||||||||
| runs-on: ubuntu-latest | ||||||||||||||||
| steps: | ||||||||||||||||
| - name: Detect visual-accepted label | ||||||||||||||||
| id: accept | ||||||||||||||||
| uses: actions/github-script@v7 | ||||||||||||||||
| with: | ||||||||||||||||
| script: | | ||||||||||||||||
| const { data: labels } = await github.rest.issues.listLabelsOnIssue({ | ||||||||||||||||
| owner: context.repo.owner, | ||||||||||||||||
| repo: context.repo.repo, | ||||||||||||||||
| issue_number: context.issue.number, | ||||||||||||||||
| }); | ||||||||||||||||
| const accepted = labels.some((l) => l.name === "visual-accepted"); | ||||||||||||||||
| core.setOutput("accepted", accepted ? "true" : "false"); | ||||||||||||||||
| if (accepted) { | ||||||||||||||||
| core.info("Label visual-accepted is present; gate will pass."); | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| - name: Accept intentional visual diffs | ||||||||||||||||
| if: steps.accept.outputs.accepted == 'true' | ||||||||||||||||
| run: | | ||||||||||||||||
| echo "visual-accepted label present — clearing visual-review (no rebuild required)." | ||||||||||||||||
|
|
||||||||||||||||
| - uses: actions/checkout@v4 | ||||||||||||||||
| if: steps.accept.outputs.accepted != 'true' | ||||||||||||||||
|
|
||||||||||||||||
| - uses: pnpm/action-setup@v4 | ||||||||||||||||
| if: steps.accept.outputs.accepted != 'true' | ||||||||||||||||
| with: | ||||||||||||||||
| version: 9.15.0 | ||||||||||||||||
|
|
||||||||||||||||
| - uses: actions/setup-node@v4 | ||||||||||||||||
| if: steps.accept.outputs.accepted != 'true' | ||||||||||||||||
| with: | ||||||||||||||||
| node-version: 24 | ||||||||||||||||
| cache: pnpm | ||||||||||||||||
|
|
||||||||||||||||
| - name: Install | ||||||||||||||||
| if: steps.accept.outputs.accepted != 'true' | ||||||||||||||||
| run: pnpm install --frozen-lockfile | ||||||||||||||||
|
|
||||||||||||||||
| - name: Build editor package deps | ||||||||||||||||
| if: steps.accept.outputs.accepted != 'true' | ||||||||||||||||
| run: | | ||||||||||||||||
| pnpm --filter @singleton-sd/post-kit-types run build | ||||||||||||||||
| pnpm --filter @singleton-sd/post-kit-compiler run build | ||||||||||||||||
| pnpm --filter @singleton-sd/post-kit-editor run build | ||||||||||||||||
|
|
||||||||||||||||
| - name: Build Storybook | ||||||||||||||||
| if: steps.accept.outputs.accepted != 'true' | ||||||||||||||||
| run: pnpm --filter @singleton-sd/post-kit-editor build-storybook | ||||||||||||||||
|
|
||||||||||||||||
| - name: Install Playwright Chromium | ||||||||||||||||
| if: steps.accept.outputs.accepted != 'true' | ||||||||||||||||
| run: pnpm --filter @singleton-sd/post-kit-editor exec playwright install chromium --with-deps | ||||||||||||||||
|
|
||||||||||||||||
| - name: Capture visual screenshots | ||||||||||||||||
| id: capture | ||||||||||||||||
| if: steps.accept.outputs.accepted != 'true' | ||||||||||||||||
| run: pnpm --filter @singleton-sd/post-kit-editor test:visual | ||||||||||||||||
|
|
||||||||||||||||
| - name: Upload visual artifact | ||||||||||||||||
| if: steps.accept.outputs.accepted != 'true' && always() && steps.capture.outcome == 'success' | ||||||||||||||||
| uses: actions/upload-artifact@v4 | ||||||||||||||||
| with: | ||||||||||||||||
| name: editor-visual | ||||||||||||||||
| path: packages/post-kit-editor/test-results/visual | ||||||||||||||||
| if-no-files-found: ignore | ||||||||||||||||
| retention-days: 14 | ||||||||||||||||
|
|
||||||||||||||||
| - name: Stage visual report for GitHub Pages | ||||||||||||||||
| id: stage | ||||||||||||||||
| if: steps.accept.outputs.accepted != 'true' && always() && steps.capture.outcome == 'success' | ||||||||||||||||
| run: | | ||||||||||||||||
| set -euo pipefail | ||||||||||||||||
| rm -rf visual-pages | ||||||||||||||||
| mkdir -p visual-pages/visual | ||||||||||||||||
| cp -R packages/post-kit-editor/test-results/visual/. visual-pages/visual/ | ||||||||||||||||
| # Root of the PR preview redirects to /visual/ | ||||||||||||||||
| printf '%s\n' \ | ||||||||||||||||
| '<!doctype html>' \ | ||||||||||||||||
| '<meta charset="utf-8" />' \ | ||||||||||||||||
| '<meta http-equiv="refresh" content="0;url=visual/" />' \ | ||||||||||||||||
| '<title>PostKit visual report</title>' \ | ||||||||||||||||
| '<p><a href="visual/">Open visual report</a></p>' \ | ||||||||||||||||
| > visual-pages/index.html | ||||||||||||||||
| touch visual-pages/.nojekyll | ||||||||||||||||
| echo "visual_url=https://singleton-sd.github.io/post-kit/pr-preview/pr-${{ github.event.number }}/visual/" >> "$GITHUB_OUTPUT" | ||||||||||||||||
|
|
||||||||||||||||
| - name: Deploy visual report to GitHub Pages | ||||||||||||||||
| id: deploy | ||||||||||||||||
| if: steps.accept.outputs.accepted != 'true' && always() && steps.stage.outcome == 'success' | ||||||||||||||||
| continue-on-error: true | ||||||||||||||||
| uses: rossjrw/pr-preview-action@v1.8.1 | ||||||||||||||||
| with: | ||||||||||||||||
| source-dir: visual-pages | ||||||||||||||||
| preview-branch: gh-pages | ||||||||||||||||
| pages-base-url: https://singleton-sd.github.io/post-kit | ||||||||||||||||
| umbrella-dir: pr-preview | ||||||||||||||||
| comment: false | ||||||||||||||||
|
|
||||||||||||||||
| - name: Retry deploy visual report | ||||||||||||||||
| id: deploy_retry | ||||||||||||||||
| if: steps.accept.outputs.accepted != 'true' && always() && steps.deploy.outcome == 'failure' | ||||||||||||||||
| uses: rossjrw/pr-preview-action@v1.8.1 | ||||||||||||||||
| with: | ||||||||||||||||
| source-dir: visual-pages | ||||||||||||||||
| preview-branch: gh-pages | ||||||||||||||||
| pages-base-url: https://singleton-sd.github.io/post-kit | ||||||||||||||||
| umbrella-dir: pr-preview | ||||||||||||||||
| comment: false | ||||||||||||||||
|
|
||||||||||||||||
| - name: Comment visual review link on PR | ||||||||||||||||
| if: steps.accept.outputs.accepted != 'true' && always() && steps.capture.outcome == 'success' | ||||||||||||||||
| uses: actions/github-script@v7 | ||||||||||||||||
|
Comment on lines
+95
to
+97
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. 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win 🔎 Supported by static analysis🤖 get_repo_knowledge executed:
Length of output: 3636 🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- workflow ---'
cat -n .github/workflows/visual-review.yml | sed -n '1,170p'
printf '%s\n' '--- relevant workflow references ---'
rg -n -C 3 'visual-accepted|Comment visual review link|actions/github-script|continue-on-error|on:|synchronize|labeled|pull_request' .github/workflows/visual-review.ymlRepository: singleton-sd/post-kit Length of output: 9828 🌐 Web query:
💡 Result: In GitHub Actions, the Citations:
Do not let the PR comment step override the visual gate. Because this step runs with Proposed fix - name: Comment visual review link on PR
if: github.event_name == 'pull_request' && steps.accept.outputs.accepted != 'true' && always()
+ continue-on-error: true
uses: actions/github-script@v7📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||
| env: | ||||||||||||||||
| VISUAL_URL: ${{ steps.stage.outputs.visual_url }} | ||||||||||||||||
| with: | ||||||||||||||||
| script: | | ||||||||||||||||
| const fs = require('fs'); | ||||||||||||||||
| const marker = '<!-- post-kit-editor-visual-review -->'; | ||||||||||||||||
| const runUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`; | ||||||||||||||||
| const artifactUrl = `${runUrl}#artifacts`; | ||||||||||||||||
| const visualUrl = | ||||||||||||||||
| process.env.VISUAL_URL || | ||||||||||||||||
| `https://singleton-sd.github.io/post-kit/pr-preview/pr-${context.issue.number}/visual/`; | ||||||||||||||||
| const manifestPath = | ||||||||||||||||
| 'packages/post-kit-editor/test-results/visual/manifest.json'; | ||||||||||||||||
| let summaryLine = '_Capture finished._'; | ||||||||||||||||
| let statusLine = ''; | ||||||||||||||||
| if (fs.existsSync(manifestPath)) { | ||||||||||||||||
| const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8')); | ||||||||||||||||
| const s = manifest.summary || {}; | ||||||||||||||||
| const changed = (s.changedStories || []).join(', ') || '—'; | ||||||||||||||||
| const neu = (s.newStories || []).join(', ') || '—'; | ||||||||||||||||
| summaryLine = [ | ||||||||||||||||
| `**${s.unchanged ?? 0}** unchanged · **${s.changed ?? 0}** changed · **${s.new ?? 0}** new`, | ||||||||||||||||
| s.changed ? `Changed: \`${changed}\`` : null, | ||||||||||||||||
| s.new ? `New: \`${neu}\`` : null, | ||||||||||||||||
| ] | ||||||||||||||||
| .filter(Boolean) | ||||||||||||||||
| .join('\n'); | ||||||||||||||||
| statusLine = s.hasDiffs | ||||||||||||||||
| ? '⚠️ Visual review needed — open the live report, then add `visual-accepted` (or update baselines).' | ||||||||||||||||
| : '✅ Visual review clear vs committed baselines.'; | ||||||||||||||||
| } | ||||||||||||||||
| const body = [ | ||||||||||||||||
| marker, | ||||||||||||||||
| '## Visual review (Storybook)', | ||||||||||||||||
| '', | ||||||||||||||||
| `🖼️ **[Open live visual report](${visualUrl})** (baseline · PR · diff)`, | ||||||||||||||||
| '', | ||||||||||||||||
| statusLine, | ||||||||||||||||
| '', | ||||||||||||||||
| summaryLine, | ||||||||||||||||
| '', | ||||||||||||||||
| `- Artifact backup: [\`editor-visual\`](${artifactUrl})`, | ||||||||||||||||
| `- Workflow run: ${runUrl}`, | ||||||||||||||||
| '', | ||||||||||||||||
| 'To accept intentional diffs: update `packages/post-kit-editor/visual-baselines/` from CI Linux `pr/*.png`, or add the **`visual-accepted`** label.', | ||||||||||||||||
| ].join('\n'); | ||||||||||||||||
|
|
||||||||||||||||
| const { data: comments } = await github.rest.issues.listComments({ | ||||||||||||||||
| owner: context.repo.owner, | ||||||||||||||||
| repo: context.repo.repo, | ||||||||||||||||
| issue_number: context.issue.number, | ||||||||||||||||
| per_page: 100, | ||||||||||||||||
| }); | ||||||||||||||||
| const existing = comments.find((c) => c.body && c.body.includes(marker)); | ||||||||||||||||
| if (existing) { | ||||||||||||||||
| await github.rest.issues.updateComment({ | ||||||||||||||||
| owner: context.repo.owner, | ||||||||||||||||
| repo: context.repo.repo, | ||||||||||||||||
| comment_id: existing.id, | ||||||||||||||||
| body, | ||||||||||||||||
| }); | ||||||||||||||||
| } else { | ||||||||||||||||
| await github.rest.issues.createComment({ | ||||||||||||||||
| owner: context.repo.owner, | ||||||||||||||||
| repo: context.repo.repo, | ||||||||||||||||
| issue_number: context.issue.number, | ||||||||||||||||
| body, | ||||||||||||||||
| }); | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| # Gate last so the live report is deployed even when review is required. | ||||||||||||||||
| - name: Visual gate | ||||||||||||||||
| if: steps.accept.outputs.accepted != 'true' | ||||||||||||||||
| env: | ||||||||||||||||
| VISUAL_ACCEPTED: '0' | ||||||||||||||||
| run: pnpm --filter @singleton-sd/post-kit-editor test:visual:gate | ||||||||||||||||
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.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🔎 Supported by static analysis
🤖 get_repo_knowledge executed:
get_repo_knowledge singleton-sd/post-kit /tmp/coderabbit-repo-knowledge/singleton-sd-post-kit-c40aa8a1/learningsLength of output: 2073
🏁 Script executed:
Repository: singleton-sd/post-kit
Length of output: 3941
🏁 Script executed:
Repository: singleton-sd/post-kit
Length of output: 3885
🤖 get_repo_knowledge executed:
get_repo_knowledge singleton-sd/post-kit /tmp/coderabbit-repo-knowledge/singleton-sd-post-kit-c40aa8a1/learningsLength of output: 2073
🏁 Script executed:
Repository: singleton-sd/post-kit
Length of output: 308
🏁 Script executed:
Repository: singleton-sd/post-kit
Length of output: 8861
Make the accepted-label run cancel the active capture.
When
visual-acceptedis added after the capture’s initial label lookup,steps.accept.outputs.acceptedremainsfalse. The capture then updates the report and runs the gate withVISUAL_ACCEPTED: '0'. Line 9 assigns the capture and label run to different groups, socancel-in-progress: truedoes not cancel the capture. The label run can succeed while the capture fails and leaves a stale report. Use one per-PR group for all events, or re-check the label before the report and gate. Keep the accepted-label path free of the main CI rerun.🤖 Prompt for AI Agents
Source: Learnings