From ebd86de1cd8baf720b0bdf4767e989785686312a Mon Sep 17 00:00:00 2001 From: MarioCadenas Date: Fri, 24 Jul 2026 11:09:50 +0200 Subject: [PATCH 1/6] ci: comment a run-anywhere command to try the PR template artifact MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The PR Template Artifact job builds a scaffold with this PR's SDK tarballs but leaves it in the artifacts UI with no obvious way to consume it. Add a sticky PR comment that prints a self-contained command โ€” gh run download of this run's artifact, unzip, then databricks apps init --template โ€” with the run id, repo, artifact name, and output dir all baked in, so it can be pasted into any folder without a repo checkout. Reuses the existing github-script + marker-based upsert pattern (bundle-size, breaking-change). Adds job-level pull-requests: write for the comment. Signed-off-by: MarioCadenas --- .../upsert-template-artifact-comment.cjs | 44 +++++++++++++++++++ .github/workflows/ci.yml | 42 ++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100644 .github/scripts/upsert-template-artifact-comment.cjs diff --git a/.github/scripts/upsert-template-artifact-comment.cjs b/.github/scripts/upsert-template-artifact-comment.cjs new file mode 100644 index 000000000..44360b2b1 --- /dev/null +++ b/.github/scripts/upsert-template-artifact-comment.cjs @@ -0,0 +1,44 @@ +/** + * Upserts a sticky PR comment with a self-contained command to scaffold a new + * app from this PR's template artifact. + * + * Invoked via `actions/github-script`. The comment body is read from the file + * at COMMENT_PATH (built by the workflow step so the run id, artifact name, and + * output dir are interpolated from CI context). The body carries the marker + * below as its first line, which we reuse to find and update an existing + * comment instead of posting a new one on every run. + */ + +const fs = require("node:fs"); + +const MARKER = ""; + +module.exports = async ({ github, context }) => { + const { owner, repo } = context.repo; + const issue_number = context.issue.number; + const body = fs.readFileSync(process.env.COMMENT_PATH, "utf-8"); + + const comments = await github.paginate(github.rest.issues.listComments, { + owner, + repo, + issue_number, + per_page: 100, + }); + const existing = comments.find((c) => c.body?.includes(MARKER)); + + if (existing) { + await github.rest.issues.updateComment({ + owner, + repo, + comment_id: existing.id, + body, + }); + } else { + await github.rest.issues.createComment({ + owner, + repo, + issue_number, + body, + }); + } +}; diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dcf84f5c9..798df405b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -171,6 +171,11 @@ jobs: runs-on: group: databricks-protected-runner-group labels: linux-ubuntu-latest + # Override the top-level `pull-requests: read` so the sticky comment step + # can create/update the "try this template" comment. + permissions: + contents: read + pull-requests: write steps: - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 @@ -226,6 +231,43 @@ jobs: name: appkit-template-${{ steps.version.outputs.version }} path: appkit-template-${{ steps.version.outputs.version }}.zip + # Build a self-contained, run-anywhere command that downloads this run's + # artifact and scaffolds an app from it. Every value (run id, repo, + # artifact name, output dir) is baked in so the reader can paste it into + # any folder โ€” no repo checkout or specific cwd required. + - name: Build "try this template" comment + env: + VERSION: ${{ steps.version.outputs.version }} + RUN_ID: ${{ github.run_id }} + REPO: ${{ github.repository }} + PR_NUMBER: ${{ github.event.pull_request.number }} + run: | + ARTIFACT="appkit-template-${VERSION}" + OUT="appkit-pr-${PR_NUMBER}" + { + echo "" + echo "### ๐Ÿ“ฆ Try this PR's app template" + echo "" + echo "Scaffolds a new app from this PR's SDK build. Run it in **any** folder (requires the GitHub CLI โ€” \`gh auth login\` โ€” and the Databricks CLI):" + echo "" + echo '```bash' + echo "gh run download ${RUN_ID} -R ${REPO} -n ${ARTIFACT} -D ${OUT} \\" + echo " && unzip -o \"${OUT}/${ARTIFACT}.zip\" -d \"${OUT}\" \\" + echo " && databricks apps init --template \"${OUT}\"" + echo '```' + echo "" + echo "The template pins \`@databricks/appkit\` and \`@databricks/appkit-ui\` to tarballs built from this branch, so the scaffolded app runs against this PR's code." + } > template-artifact-comment.md + + - name: Upsert "try this template" PR comment + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + COMMENT_PATH: template-artifact-comment.md + with: + script: | + const upsert = require('./.github/scripts/upsert-template-artifact-comment.cjs'); + await upsert({ github, context }); + docs-build: name: Docs Build needs: detect-changes From f5a798674d4c49dd167efe1d255f21ff8047c866 Mon Sep 17 00:00:00 2001 From: MarioCadenas Date: Fri, 24 Jul 2026 11:19:12 +0200 Subject: [PATCH 2/6] ci: restore id-token permission on pr-template-artifact job Job-level permissions replace the top-level set rather than merging, so adding pull-requests: write dropped the top-level id-token: write that setup-jfrog-npm needs for its OIDC token (ACTIONS_ID_TOKEN_REQUEST_TOKEN unbound). List all three at the job level. Signed-off-by: MarioCadenas --- .github/workflows/ci.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 798df405b..5079413c5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -171,11 +171,13 @@ jobs: runs-on: group: databricks-protected-runner-group labels: linux-ubuntu-latest - # Override the top-level `pull-requests: read` so the sticky comment step - # can create/update the "try this template" comment. + # Job-level permissions REPLACE (not merge with) the top-level set, so all + # three must be listed: pull-requests: write for the sticky comment step, + # and id-token: write which setup-jfrog-npm needs for its OIDC token. permissions: contents: read pull-requests: write + id-token: write steps: - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 From de4085bc7d8f0aa991d56e8b48e916fb8fa39aa4 Mon Sep 17 00:00:00 2001 From: MarioCadenas Date: Fri, 24 Jul 2026 11:43:33 +0200 Subject: [PATCH 3/6] ci: skip try-template comment on fork PRs Fork PRs get a read-only GITHUB_TOKEN, so the github-script upsert would 403 and fail the whole pr-template-artifact job for an author who can't fix it. Guard the comment step with the same head-repo check bundle-size.yml and eval-pr-comment.yml already use. Signed-off-by: MarioCadenas --- .github/workflows/ci.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5079413c5..f473af9bf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -261,7 +261,11 @@ jobs: echo "The template pins \`@databricks/appkit\` and \`@databricks/appkit-ui\` to tarballs built from this branch, so the scaffolded app runs against this PR's code." } > template-artifact-comment.md + # Fork PRs get a read-only GITHUB_TOKEN, so commenting would 403 and fail + # a job the author can't fix. Skip for forks โ€” the command is still in the + # job log above. Matches bundle-size.yml / eval-pr-comment.yml. - name: Upsert "try this template" PR comment + if: github.event.pull_request.head.repo.full_name == github.repository uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: COMMENT_PATH: template-artifact-comment.md From f4b9a858f3eb0bfa91edc2c100e150dab688ca90 Mon Sep 17 00:00:00 2001 From: MarioCadenas Date: Fri, 24 Jul 2026 12:18:09 +0200 Subject: [PATCH 4/6] ci: merge eval link and template command into one PR comment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Combine the evals-monitor link and the try-this-template command into a single sticky comment with two delimited sections instead of two separate comments. Add a shared upsert-pr-comment-section.cjs helper: each workflow owns one section id and rewrites only its block, preserving the other's content (a push reruns the template section without wiping the eval link). The two writers are separated in time โ€” the eval link posts instantly on PR open, the template command posts after the ~5-min build โ€” so concurrent writes to the shared comment don't occur. Bundle-size and breaking-change comments are intentionally left separate for now. Replaces the single-purpose upsert-template-artifact-comment.cjs and rewires eval-pr-comment.yml onto the shared helper. Signed-off-by: MarioCadenas --- .github/scripts/upsert-pr-comment-section.cjs | 91 +++++++++++++++++++ .../upsert-template-artifact-comment.cjs | 44 --------- .github/workflows/ci.yml | 16 ++-- .github/workflows/eval-pr-comment.yml | 53 +++++------ 4 files changed, 122 insertions(+), 82 deletions(-) create mode 100644 .github/scripts/upsert-pr-comment-section.cjs delete mode 100644 .github/scripts/upsert-template-artifact-comment.cjs diff --git a/.github/scripts/upsert-pr-comment-section.cjs b/.github/scripts/upsert-pr-comment-section.cjs new file mode 100644 index 000000000..8b1db480b --- /dev/null +++ b/.github/scripts/upsert-pr-comment-section.cjs @@ -0,0 +1,91 @@ +/** + * Upserts one section of the shared "PR bot" sticky comment. + * + * Multiple workflows contribute sections to a single comment marked with + * MARKER (currently: the evals-monitor link and the try-this-template command). + * Each caller owns one SECTION_ID and rewrites only its delimited block, so the + * workflows preserve each other's content instead of clobbering it โ€” a plain + * push reruns the template job without wiping the eval link, and vice versa. + * Sections are always re-rendered in SECTION_ORDER for a stable layout + * regardless of which workflow writes first. + * + * This is a read-modify-write on one comment. The two current callers are + * separated in time (eval link posts on PR open; the template command posts + * after the ~5-min build), so concurrent writes are effectively impossible. + * Adding a section that races the others would need real conflict handling. + * + * Invoked via `actions/github-script`. Env: + * SECTION_ID - which section this caller owns (must be in SECTION_ORDER) + * SECTION_PATH - file containing the section's markdown body + */ + +const fs = require("node:fs"); + +const MARKER = ""; +// Canonical top-to-bottom order of sections in the unified comment. +const SECTION_ORDER = ["evals", "template"]; + +const openTag = (id) => ``; +const closeTag = (id) => ``; + +function parseSections(body) { + const out = {}; + for (const id of SECTION_ORDER) { + const start = body.indexOf(openTag(id)); + const end = body.indexOf(closeTag(id)); + if (start !== -1 && end !== -1 && end > start) { + out[id] = body.slice(start + openTag(id).length, end).trim(); + } + } + return out; +} + +function render(sections) { + const parts = [MARKER]; + for (const id of SECTION_ORDER) { + if (sections[id] == null) continue; + parts.push(`${openTag(id)}\n${sections[id]}\n${closeTag(id)}`); + } + return parts.join("\n\n"); +} + +module.exports = async ({ github, context }) => { + const { owner, repo } = context.repo; + const issue_number = context.issue.number; + + const id = process.env.SECTION_ID; + if (!SECTION_ORDER.includes(id)) { + throw new Error( + `Unknown SECTION_ID "${id}"; expected one of ${SECTION_ORDER.join(", ")}`, + ); + } + const sectionBody = fs.readFileSync(process.env.SECTION_PATH, "utf-8").trim(); + + const comments = await github.paginate(github.rest.issues.listComments, { + owner, + repo, + issue_number, + per_page: 100, + }); + const existing = comments.find((c) => c.body?.includes(MARKER)); + + const sections = existing ? parseSections(existing.body) : {}; + sections[id] = sectionBody; + const body = render(sections); + + if (existing) { + await github.rest.issues.updateComment({ + owner, + repo, + comment_id: existing.id, + body, + }); + } else { + await github.rest.issues.createComment({ + owner, + repo, + issue_number, + body, + }); + } +}; diff --git a/.github/scripts/upsert-template-artifact-comment.cjs b/.github/scripts/upsert-template-artifact-comment.cjs deleted file mode 100644 index 44360b2b1..000000000 --- a/.github/scripts/upsert-template-artifact-comment.cjs +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Upserts a sticky PR comment with a self-contained command to scaffold a new - * app from this PR's template artifact. - * - * Invoked via `actions/github-script`. The comment body is read from the file - * at COMMENT_PATH (built by the workflow step so the run id, artifact name, and - * output dir are interpolated from CI context). The body carries the marker - * below as its first line, which we reuse to find and update an existing - * comment instead of posting a new one on every run. - */ - -const fs = require("node:fs"); - -const MARKER = ""; - -module.exports = async ({ github, context }) => { - const { owner, repo } = context.repo; - const issue_number = context.issue.number; - const body = fs.readFileSync(process.env.COMMENT_PATH, "utf-8"); - - const comments = await github.paginate(github.rest.issues.listComments, { - owner, - repo, - issue_number, - per_page: 100, - }); - const existing = comments.find((c) => c.body?.includes(MARKER)); - - if (existing) { - await github.rest.issues.updateComment({ - owner, - repo, - comment_id: existing.id, - body, - }); - } else { - await github.rest.issues.createComment({ - owner, - repo, - issue_number, - body, - }); - } -}; diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f473af9bf..93b55c3e5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -236,8 +236,10 @@ jobs: # Build a self-contained, run-anywhere command that downloads this run's # artifact and scaffolds an app from it. Every value (run id, repo, # artifact name, output dir) is baked in so the reader can paste it into - # any folder โ€” no repo checkout or specific cwd required. - - name: Build "try this template" comment + # any folder โ€” no repo checkout or specific cwd required. This is the + # `template` section of the shared PR-bot comment; the section markers are + # added by upsert-pr-comment-section.cjs, not here. + - name: Build "try this template" comment section env: VERSION: ${{ steps.version.outputs.version }} RUN_ID: ${{ github.run_id }} @@ -247,7 +249,6 @@ jobs: ARTIFACT="appkit-template-${VERSION}" OUT="appkit-pr-${PR_NUMBER}" { - echo "" echo "### ๐Ÿ“ฆ Try this PR's app template" echo "" echo "Scaffolds a new app from this PR's SDK build. Run it in **any** folder (requires the GitHub CLI โ€” \`gh auth login\` โ€” and the Databricks CLI):" @@ -259,19 +260,20 @@ jobs: echo '```' echo "" echo "The template pins \`@databricks/appkit\` and \`@databricks/appkit-ui\` to tarballs built from this branch, so the scaffolded app runs against this PR's code." - } > template-artifact-comment.md + } > template-section.md # Fork PRs get a read-only GITHUB_TOKEN, so commenting would 403 and fail # a job the author can't fix. Skip for forks โ€” the command is still in the # job log above. Matches bundle-size.yml / eval-pr-comment.yml. - - name: Upsert "try this template" PR comment + - name: Upsert template section into PR comment if: github.event.pull_request.head.repo.full_name == github.repository uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: - COMMENT_PATH: template-artifact-comment.md + SECTION_ID: template + SECTION_PATH: template-section.md with: script: | - const upsert = require('./.github/scripts/upsert-template-artifact-comment.cjs'); + const upsert = require('./.github/scripts/upsert-pr-comment-section.cjs'); await upsert({ github, context }); docs-build: diff --git a/.github/workflows/eval-pr-comment.yml b/.github/workflows/eval-pr-comment.yml index 9abcdd91b..3e64abb7c 100644 --- a/.github/workflows/eval-pr-comment.yml +++ b/.github/workflows/eval-pr-comment.yml @@ -1,9 +1,9 @@ name: Eval PR Comment -# Posts a small comment on every new PR with a link to the evals-monitor app, -# where the author can start an eval for their PR. We post a link rather than -# triggering the job from CI because GitHub Actions runners cannot reach the -# dogfood.staging workspace (network perimeter). +# Contributes the `evals` section (a link to the evals-monitor app, where the +# author can start an eval for their PR) to the shared PR-bot comment. We post a +# link rather than triggering the job from CI because GitHub Actions runners +# cannot reach the dogfood.staging workspace (network perimeter). on: pull_request: types: [opened, reopened] @@ -13,10 +13,9 @@ permissions: contents: read pull-requests: write -# Serialize per-PR so a rapid open+reopen can't race the marker check and -# double-post. cancel-in-progress: false โ€” the queued run still executes and -# deterministically sees the prior run's comment (then skips); there's no -# "latest commit wins" semantics here, the posted link is identical either way. +# Serialize per-PR so a rapid open+reopen can't race the section upsert. +# cancel-in-progress: false โ€” the queued run still executes and re-writes an +# identical section, which is harmless. concurrency: group: eval-pr-comment-${{ github.event.pull_request.number }} cancel-in-progress: false @@ -32,29 +31,21 @@ jobs: group: databricks-protected-runner-group labels: linux-ubuntu-latest steps: - - name: Post eval link comment + # Checkout so the github-script step can require the shared section helper. + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + - name: Build eval link comment section + env: + PR_NUMBER: ${{ github.event.pull_request.number }} + run: | + URL="https://evals-monitor-6051921418418893.staging.aws.databricksapps.com/prs/appkit/${PR_NUMBER}" + echo "> ๐Ÿ”ฌ  **Run evals on this PR**  ยท  [**Go to Evals Monitor โ†’**](${URL})" > eval-section.md + + - name: Upsert eval section into PR comment uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + SECTION_ID: evals + SECTION_PATH: eval-section.md with: script: | - const marker = ""; - const { owner, repo } = context.repo; - const issue_number = context.issue.number; - const url = `https://evals-monitor-6051921418418893.staging.aws.databricksapps.com/prs/appkit/${issue_number}`; - - // Idempotent: skip if we've already commented on this PR. - const comments = await github.paginate(github.rest.issues.listComments, { - owner, - repo, - issue_number, - per_page: 100, - }); - if (comments.some((c) => c.body?.includes(marker))) return; - - const body = `${marker}\n> ๐Ÿ”ฌ  **Run evals on this PR**  ยท  [**Go to Evals Monitor โ†’**](${url})`; - - await github.rest.issues.createComment({ - owner, - repo, - issue_number, - body, - }); + const upsert = require('./.github/scripts/upsert-pr-comment-section.cjs'); + await upsert({ github, context }); From 8c41b0cb0636ed66eb804ba0d2c52efc22f4c96b Mon Sep 17 00:00:00 2001 From: MarioCadenas Date: Fri, 24 Jul 2026 12:20:53 +0200 Subject: [PATCH 5/6] ci: fold eval link into the template artifact comment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drop the separate Eval PR Comment workflow and the section-merge helper; the pr-template-artifact job now posts a single comment containing both the evals-monitor link and the run-anywhere template command. One writer, one comment, plain marker-based upsert โ€” no cross-workflow coordination. Tradeoff: the eval link now rides the ~5-min build and is skipped on docs-only PRs (where the artifact job doesn't run and evals are moot), instead of posting instantly on every PR open. Signed-off-by: MarioCadenas --- .github/scripts/upsert-pr-bot-comment.cjs | 45 +++++++++ .github/scripts/upsert-pr-comment-section.cjs | 91 ------------------- .github/workflows/ci.yml | 28 +++--- .github/workflows/eval-pr-comment.yml | 51 ----------- 4 files changed, 60 insertions(+), 155 deletions(-) create mode 100644 .github/scripts/upsert-pr-bot-comment.cjs delete mode 100644 .github/scripts/upsert-pr-comment-section.cjs delete mode 100644 .github/workflows/eval-pr-comment.yml diff --git a/.github/scripts/upsert-pr-bot-comment.cjs b/.github/scripts/upsert-pr-bot-comment.cjs new file mode 100644 index 000000000..66fe18abd --- /dev/null +++ b/.github/scripts/upsert-pr-bot-comment.cjs @@ -0,0 +1,45 @@ +/** + * Upserts the sticky "PR bot" comment built by the pr-template-artifact job: + * the evals-monitor link plus the run-anywhere command to scaffold an app from + * this PR's template artifact. + * + * Invoked via `actions/github-script`. The comment body is read from the file + * at COMMENT_PATH (built by the workflow step so the run id, artifact name, and + * output dir are interpolated from CI context). The body carries the marker + * below as its first line, which we reuse to find and update an existing + * comment instead of posting a new one on every push. + */ + +const fs = require("node:fs"); + +const MARKER = ""; + +module.exports = async ({ github, context }) => { + const { owner, repo } = context.repo; + const issue_number = context.issue.number; + const body = fs.readFileSync(process.env.COMMENT_PATH, "utf-8"); + + const comments = await github.paginate(github.rest.issues.listComments, { + owner, + repo, + issue_number, + per_page: 100, + }); + const existing = comments.find((c) => c.body?.includes(MARKER)); + + if (existing) { + await github.rest.issues.updateComment({ + owner, + repo, + comment_id: existing.id, + body, + }); + } else { + await github.rest.issues.createComment({ + owner, + repo, + issue_number, + body, + }); + } +}; diff --git a/.github/scripts/upsert-pr-comment-section.cjs b/.github/scripts/upsert-pr-comment-section.cjs deleted file mode 100644 index 8b1db480b..000000000 --- a/.github/scripts/upsert-pr-comment-section.cjs +++ /dev/null @@ -1,91 +0,0 @@ -/** - * Upserts one section of the shared "PR bot" sticky comment. - * - * Multiple workflows contribute sections to a single comment marked with - * MARKER (currently: the evals-monitor link and the try-this-template command). - * Each caller owns one SECTION_ID and rewrites only its delimited block, so the - * workflows preserve each other's content instead of clobbering it โ€” a plain - * push reruns the template job without wiping the eval link, and vice versa. - * Sections are always re-rendered in SECTION_ORDER for a stable layout - * regardless of which workflow writes first. - * - * This is a read-modify-write on one comment. The two current callers are - * separated in time (eval link posts on PR open; the template command posts - * after the ~5-min build), so concurrent writes are effectively impossible. - * Adding a section that races the others would need real conflict handling. - * - * Invoked via `actions/github-script`. Env: - * SECTION_ID - which section this caller owns (must be in SECTION_ORDER) - * SECTION_PATH - file containing the section's markdown body - */ - -const fs = require("node:fs"); - -const MARKER = ""; -// Canonical top-to-bottom order of sections in the unified comment. -const SECTION_ORDER = ["evals", "template"]; - -const openTag = (id) => ``; -const closeTag = (id) => ``; - -function parseSections(body) { - const out = {}; - for (const id of SECTION_ORDER) { - const start = body.indexOf(openTag(id)); - const end = body.indexOf(closeTag(id)); - if (start !== -1 && end !== -1 && end > start) { - out[id] = body.slice(start + openTag(id).length, end).trim(); - } - } - return out; -} - -function render(sections) { - const parts = [MARKER]; - for (const id of SECTION_ORDER) { - if (sections[id] == null) continue; - parts.push(`${openTag(id)}\n${sections[id]}\n${closeTag(id)}`); - } - return parts.join("\n\n"); -} - -module.exports = async ({ github, context }) => { - const { owner, repo } = context.repo; - const issue_number = context.issue.number; - - const id = process.env.SECTION_ID; - if (!SECTION_ORDER.includes(id)) { - throw new Error( - `Unknown SECTION_ID "${id}"; expected one of ${SECTION_ORDER.join(", ")}`, - ); - } - const sectionBody = fs.readFileSync(process.env.SECTION_PATH, "utf-8").trim(); - - const comments = await github.paginate(github.rest.issues.listComments, { - owner, - repo, - issue_number, - per_page: 100, - }); - const existing = comments.find((c) => c.body?.includes(MARKER)); - - const sections = existing ? parseSections(existing.body) : {}; - sections[id] = sectionBody; - const body = render(sections); - - if (existing) { - await github.rest.issues.updateComment({ - owner, - repo, - comment_id: existing.id, - body, - }); - } else { - await github.rest.issues.createComment({ - owner, - repo, - issue_number, - body, - }); - } -}; diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 93b55c3e5..d47076499 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -233,13 +233,12 @@ jobs: name: appkit-template-${{ steps.version.outputs.version }} path: appkit-template-${{ steps.version.outputs.version }}.zip - # Build a self-contained, run-anywhere command that downloads this run's - # artifact and scaffolds an app from it. Every value (run id, repo, - # artifact name, output dir) is baked in so the reader can paste it into - # any folder โ€” no repo checkout or specific cwd required. This is the - # `template` section of the shared PR-bot comment; the section markers are - # added by upsert-pr-comment-section.cjs, not here. - - name: Build "try this template" comment section + # Build the PR-bot comment: a link to the evals-monitor app, plus a + # self-contained, run-anywhere command that downloads this run's artifact + # and scaffolds an app from it. Every value (run id, repo, artifact name, + # output dir) is baked in so the reader can paste it into any folder โ€” no + # repo checkout or specific cwd required. + - name: Build PR-bot comment env: VERSION: ${{ steps.version.outputs.version }} RUN_ID: ${{ github.run_id }} @@ -248,7 +247,11 @@ jobs: run: | ARTIFACT="appkit-template-${VERSION}" OUT="appkit-pr-${PR_NUMBER}" + EVALS_URL="https://evals-monitor-6051921418418893.staging.aws.databricksapps.com/prs/appkit/${PR_NUMBER}" { + echo "" + echo "> ๐Ÿ”ฌ  **Run evals on this PR**  ยท  [**Go to Evals Monitor โ†’**](${EVALS_URL})" + echo "" echo "### ๐Ÿ“ฆ Try this PR's app template" echo "" echo "Scaffolds a new app from this PR's SDK build. Run it in **any** folder (requires the GitHub CLI โ€” \`gh auth login\` โ€” and the Databricks CLI):" @@ -260,20 +263,19 @@ jobs: echo '```' echo "" echo "The template pins \`@databricks/appkit\` and \`@databricks/appkit-ui\` to tarballs built from this branch, so the scaffolded app runs against this PR's code." - } > template-section.md + } > pr-bot-comment.md # Fork PRs get a read-only GITHUB_TOKEN, so commenting would 403 and fail # a job the author can't fix. Skip for forks โ€” the command is still in the - # job log above. Matches bundle-size.yml / eval-pr-comment.yml. - - name: Upsert template section into PR comment + # job log above. Matches bundle-size.yml. + - name: Upsert PR-bot comment if: github.event.pull_request.head.repo.full_name == github.repository uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: - SECTION_ID: template - SECTION_PATH: template-section.md + COMMENT_PATH: pr-bot-comment.md with: script: | - const upsert = require('./.github/scripts/upsert-pr-comment-section.cjs'); + const upsert = require('./.github/scripts/upsert-pr-bot-comment.cjs'); await upsert({ github, context }); docs-build: diff --git a/.github/workflows/eval-pr-comment.yml b/.github/workflows/eval-pr-comment.yml deleted file mode 100644 index 3e64abb7c..000000000 --- a/.github/workflows/eval-pr-comment.yml +++ /dev/null @@ -1,51 +0,0 @@ -name: Eval PR Comment - -# Contributes the `evals` section (a link to the evals-monitor app, where the -# author can start an eval for their PR) to the shared PR-bot comment. We post a -# link rather than triggering the job from CI because GitHub Actions runners -# cannot reach the dogfood.staging workspace (network perimeter). -on: - pull_request: - types: [opened, reopened] - branches: [main] - -permissions: - contents: read - pull-requests: write - -# Serialize per-PR so a rapid open+reopen can't race the section upsert. -# cancel-in-progress: false โ€” the queued run still executes and re-writes an -# identical section, which is harmless. -concurrency: - group: eval-pr-comment-${{ github.event.pull_request.number }} - cancel-in-progress: false - -jobs: - post-eval-link: - name: Post eval link comment - # Skip fork PRs: their GITHUB_TOKEN is read-only (createComment would 403), - # and the evals-monitor app is internal โ€” the link is useless to external - # contributors anyway. - if: github.event.pull_request.head.repo.full_name == github.repository - runs-on: - group: databricks-protected-runner-group - labels: linux-ubuntu-latest - steps: - # Checkout so the github-script step can require the shared section helper. - - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - - name: Build eval link comment section - env: - PR_NUMBER: ${{ github.event.pull_request.number }} - run: | - URL="https://evals-monitor-6051921418418893.staging.aws.databricksapps.com/prs/appkit/${PR_NUMBER}" - echo "> ๐Ÿ”ฌ  **Run evals on this PR**  ยท  [**Go to Evals Monitor โ†’**](${URL})" > eval-section.md - - - name: Upsert eval section into PR comment - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 - env: - SECTION_ID: evals - SECTION_PATH: eval-section.md - with: - script: | - const upsert = require('./.github/scripts/upsert-pr-comment-section.cjs'); - await upsert({ github, context }); From 78332ee00060c6fe49f350d8c6afec86fcfc4ad3 Mon Sep 17 00:00:00 2001 From: MarioCadenas Date: Fri, 24 Jul 2026 12:35:21 +0200 Subject: [PATCH 6/6] ci: give the PR-bot comment a header and parallel section titles MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add an overall '๐Ÿค– AppKit PR bot' header and a '### ๐Ÿ”ฌ Run evals' title so the evals link matches the template section's heading instead of being a bare blockquote. Signed-off-by: MarioCadenas --- .github/workflows/ci.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d47076499..13fd1a779 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -250,7 +250,11 @@ jobs: EVALS_URL="https://evals-monitor-6051921418418893.staging.aws.databricksapps.com/prs/appkit/${PR_NUMBER}" { echo "" - echo "> ๐Ÿ”ฌ  **Run evals on this PR**  ยท  [**Go to Evals Monitor โ†’**](${EVALS_URL})" + echo "## ๐Ÿค– AppKit PR bot" + echo "" + echo "### ๐Ÿ”ฌ Run evals" + echo "" + echo "Start an eval for this PR from the evals-monitor app: [**Go to Evals Monitor โ†’**](${EVALS_URL})" echo "" echo "### ๐Ÿ“ฆ Try this PR's app template" echo ""