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/workflows/ci.yml b/.github/workflows/ci.yml index dcf84f5c9..13fd1a779 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -171,6 +171,13 @@ jobs: runs-on: group: databricks-protected-runner-group labels: linux-ubuntu-latest + # 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 @@ -226,6 +233,55 @@ jobs: name: appkit-template-${{ steps.version.outputs.version }} path: appkit-template-${{ steps.version.outputs.version }}.zip + # 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 }} + REPO: ${{ github.repository }} + PR_NUMBER: ${{ github.event.pull_request.number }} + 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 "## ๐Ÿค– 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 "" + 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." + } > 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. + - 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: + COMMENT_PATH: pr-bot-comment.md + with: + script: | + const upsert = require('./.github/scripts/upsert-pr-bot-comment.cjs'); + await upsert({ github, context }); + docs-build: name: Docs Build needs: detect-changes diff --git a/.github/workflows/eval-pr-comment.yml b/.github/workflows/eval-pr-comment.yml deleted file mode 100644 index 9abcdd91b..000000000 --- a/.github/workflows/eval-pr-comment.yml +++ /dev/null @@ -1,60 +0,0 @@ -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). -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 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. -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: - - name: Post eval link comment - uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 - 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, - });