deps: refresh workspace dependencies and CI tooling #99
Workflow file for this run
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
| name: org-required-checks | |
| on: | |
| pull_request: | |
| types: | |
| - opened | |
| - reopened | |
| - synchronize | |
| - edited | |
| - ready_for_review | |
| deployment_status: | |
| permissions: | |
| checks: read | |
| contents: read | |
| pull-requests: read | |
| statuses: read | |
| jobs: | |
| verify: | |
| name: Check | |
| if: | | |
| github.event_name == 'pull_request' || | |
| (github.event_name == 'deployment_status' && github.event.deployment_status.state == 'success') | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Wait for merge gates | |
| uses: actions/github-script@v8 | |
| with: | |
| script: | | |
| const owner = context.repo.owner; | |
| const repo = context.repo.repo; | |
| let pr = context.payload.pull_request; | |
| let pull_number; | |
| let headSha; | |
| if (pr) { | |
| pull_number = pr.number; | |
| headSha = pr.head.sha; | |
| } else { | |
| const deploymentStatus = context.payload.deployment_status; | |
| const deploymentSha = deploymentStatus?.deployment?.sha; | |
| if (!deploymentSha) { | |
| core.info("No pull request or deployment SHA context. Passing check."); | |
| return; | |
| } | |
| headSha = deploymentSha; | |
| const associatedPrs = await github.rest.repos.listPullRequestsAssociatedWithCommit({ | |
| owner, | |
| repo, | |
| commit_sha: headSha, | |
| }); | |
| pr = | |
| associatedPrs.data.find((candidate) => candidate.state === "open") ?? null; | |
| if (!pr) { | |
| core.info(`No open pull request associated with SHA ${headSha}. Passing check.`); | |
| return; | |
| } | |
| pull_number = pr.number; | |
| } | |
| const files = await github.paginate(github.rest.pulls.listFiles, { | |
| owner, | |
| repo, | |
| pull_number, | |
| per_page: 100, | |
| }); | |
| const ciIgnoredPatterns = [ | |
| /^docs\//, | |
| /^README\.md$/, | |
| /^AGENTS\.md$/, | |
| /^\.codex\//, | |
| /^\.opencode\//, | |
| ]; | |
| const hasCiRelevantChange = files.some((file) => { | |
| return !ciIgnoredPatterns.some((pattern) => pattern.test(file.filename)); | |
| }); | |
| const isReleaseVersionBumpPr = | |
| /^chore\/bump-version-/.test(pr.head.ref) && | |
| (pr.user?.login === "github-actions[bot]" || pr.user?.login === "app/github-actions"); | |
| const requiredContexts = ["Analyze (JavaScript/TypeScript)", "Vercel"]; | |
| if (hasCiRelevantChange && !isReleaseVersionBumpPr) { | |
| requiredContexts.push("checks", "Preview validation (seed + runtime QA)"); | |
| } | |
| core.info( | |
| `Required contexts: ${requiredContexts.join(", ")} (ciRelevant=${hasCiRelevantChange}, releaseBump=${isReleaseVersionBumpPr})`, | |
| ); | |
| const deadline = Date.now() + 20 * 60 * 1000; | |
| const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); | |
| const normalizeStatusContext = (state) => { | |
| if (state === "success") { | |
| return "success"; | |
| } | |
| if (state === "failure" || state === "error") { | |
| return "failure"; | |
| } | |
| return "pending"; | |
| }; | |
| const normalizeCheckRun = (checkRun) => { | |
| if (checkRun.status !== "completed") { | |
| return "pending"; | |
| } | |
| if (checkRun.conclusion === "success") { | |
| return "success"; | |
| } | |
| if (checkRun.conclusion === "neutral") { | |
| return "success"; | |
| } | |
| return "failure"; | |
| }; | |
| while (Date.now() < deadline) { | |
| const [checkRunsResp, statusesResp] = await Promise.all([ | |
| github.rest.checks.listForRef({ | |
| owner, | |
| repo, | |
| ref: headSha, | |
| per_page: 100, | |
| }), | |
| github.rest.repos.listCommitStatusesForRef({ | |
| owner, | |
| repo, | |
| ref: headSha, | |
| per_page: 100, | |
| }), | |
| ]); | |
| const checkRunsByName = new Map(); | |
| for (const checkRun of checkRunsResp.data.check_runs) { | |
| if (!checkRunsByName.has(checkRun.name)) { | |
| checkRunsByName.set(checkRun.name, checkRun); | |
| } | |
| } | |
| const statusesByContext = new Map(); | |
| for (const status of statusesResp.data) { | |
| if (!statusesByContext.has(status.context)) { | |
| statusesByContext.set(status.context, status); | |
| } | |
| } | |
| let hasPending = false; | |
| for (const contextName of requiredContexts) { | |
| const checkRun = checkRunsByName.get(contextName); | |
| if (checkRun) { | |
| const normalized = normalizeCheckRun(checkRun); | |
| if (normalized === "failure") { | |
| core.setFailed( | |
| `${contextName} failed with conclusion '${checkRun.conclusion ?? "unknown"}'.`, | |
| ); | |
| return; | |
| } | |
| if (normalized === "pending") { | |
| hasPending = true; | |
| } | |
| continue; | |
| } | |
| const status = statusesByContext.get(contextName); | |
| if (status) { | |
| const normalized = normalizeStatusContext(status.state); | |
| if (normalized === "failure") { | |
| core.setFailed(`${contextName} failed with state '${status.state}'.`); | |
| return; | |
| } | |
| if (normalized === "pending") { | |
| hasPending = true; | |
| } | |
| continue; | |
| } | |
| hasPending = true; | |
| } | |
| if (!hasPending) { | |
| core.info("All required contexts passed."); | |
| return; | |
| } | |
| await sleep(15000); | |
| } | |
| core.setFailed( | |
| `Timed out waiting for required contexts: ${requiredContexts.join(", ")}`, | |
| ); | |
| ValidatePrTitle: | |
| runs-on: ubuntu-latest | |
| if: github.event_name == 'pull_request' && github.event.pull_request.draft == false | |
| steps: | |
| - name: Validate PR title | |
| env: | |
| PR_TITLE: ${{ github.event.pull_request.title }} | |
| run: | | |
| if [ -z "${PR_TITLE:-}" ]; then | |
| echo "PR title is empty." | |
| exit 1 | |
| fi | |
| if [ ${#PR_TITLE} -gt 120 ]; then | |
| echo "PR title must be 120 characters or fewer." | |
| exit 1 | |
| fi | |
| echo "PR title validation passed." |