Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 11 additions & 8 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,12 @@ jobs:
# Intended as a required status check on main together with ui-smoke,
# Gitleaks, and one migration replay gate.
verify:
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
timeout-minutes: 25

steps:
- name: Checkout
uses: actions/checkout@v7
uses: actions/checkout@v6
with:
persist-credentials: false

Expand All @@ -42,6 +42,9 @@ jobs:
cache: npm
cache-dependency-path: package-lock.json

- name: Workflow action pin check
run: npm run check:github-actions

- name: Setup Deno
uses: denoland/setup-deno@v2
with:
Expand Down Expand Up @@ -99,12 +102,12 @@ jobs:
# rerun does not repeat them. The runner boots its own dev server, so no
# build step is needed here.
ui-smoke:
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
timeout-minutes: 25

steps:
- name: Checkout
uses: actions/checkout@v7
uses: actions/checkout@v6
with:
persist-credentials: false

Expand Down Expand Up @@ -146,12 +149,12 @@ jobs:
# required and reliable, keep this advisory or path-filtered rather than
# requiring both replay gates on every PR.
db-reset-verify:
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
timeout-minutes: 15

steps:
- name: Checkout
uses: actions/checkout@v7
uses: actions/checkout@v6
with:
persist-credentials: false

Expand Down Expand Up @@ -193,12 +196,12 @@ jobs:
release-browser-matrix:
if: github.event_name == 'workflow_dispatch' || github.event_name == 'schedule' || github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/')
needs: [verify, ui-smoke, db-reset-verify]
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
timeout-minutes: 70

steps:
- name: Checkout
uses: actions/checkout@v7
uses: actions/checkout@v6
with:
persist-credentials: false

Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/codex-autofix-review-comments.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ concurrency:
jobs:
request-codex-autoresolve:
name: Request Codex auto-resolve
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
if: >
github.event.pull_request.state == 'open' &&
contains(github.event.comment.user.login, 'chatgpt-codex-connector')
Expand All @@ -38,7 +38,7 @@ jobs:
if (
sourceBody.includes("codex-autoresolve:") ||
sourceBody.includes("codex-autoresolve-pr:") ||
sourceBody.includes("@codex resolve all review comments")
sourceBody.includes("@codex resolve actionable Codex review findings for this pull request and current head")
) {
core.notice("Skipping Codex auto-resolve request because the source comment already looks like an auto-resolve request.");
return;
Expand Down Expand Up @@ -75,7 +75,7 @@ jobs:
const prompt = [
marker,
`<!-- codex-autoresolve-source-review-comment:${reviewComment.id} -->`,
"@codex resolve all review comments using the repository instructions. Always fix P0 and P1 findings. For P2 and lower findings, decide whether each is worth fixing automatically. Fix clear, scoped, low-risk issues with the best minimal change; otherwise reply explaining why the issue is deferred or not actionable. After each fix or decision, resolve the review conversation if supported. Do not use external APIs, paid services, credentials, dependency changes, or broad refactors unless explicitly authorized. Add targeted tests where behavior changes and run the narrowest relevant validation.",
"@codex resolve actionable Codex review findings for this pull request and current head using the repository instructions. Always fix P0 and P1 findings. For P2 and lower findings, decide whether each is worth fixing automatically. Fix clear, scoped, low-risk issues with the best minimal change; otherwise reply explaining why the issue is deferred or not actionable. Do not update the branch from main, address unrelated reviews, broaden scope, or create more than one scoped fix commit unless explicitly asked. After each fix or decision, resolve the review conversation if supported. Do not use external APIs, paid services, credentials, dependency changes, or broad refactors unless explicitly authorized. Add targeted tests where behavior changes and run the narrowest relevant validation.",
].join("\n\n");

try {
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/docker-image.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,11 @@ permissions:

jobs:
app-image:
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
timeout-minutes: 30
steps:
- name: Checkout
uses: actions/checkout@v7
uses: actions/checkout@v6
with:
persist-credentials: false

Expand All @@ -72,11 +72,11 @@ jobs:
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY=placeholder-ci-publishable-key

worker-image:
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
timeout-minutes: 30
steps:
- name: Checkout
uses: actions/checkout@v7
uses: actions/checkout@v6
with:
persist-credentials: false

Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/eval-canary.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,12 @@ env:

jobs:
eval-canary:
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
timeout-minutes: 45

steps:
- name: Checkout
uses: actions/checkout@v7
uses: actions/checkout@v6
with:
persist-credentials: false

Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/live-drift.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,12 @@ env:

jobs:
live-drift:
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
timeout-minutes: 20

steps:
- name: Checkout
uses: actions/checkout@v7
uses: actions/checkout@v6
with:
persist-credentials: false

Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/sast.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,13 @@ jobs:
# the merge. Runs OSS-engine only — no Semgrep account or token required.
semgrep:
name: Semgrep
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
timeout-minutes: 15
container:
image: semgrep/semgrep:1.168.0
steps:
- name: Checkout
uses: actions/checkout@v7
uses: actions/checkout@v6
with:
persist-credentials: false

Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/secret-scan.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ permissions:
jobs:
gitleaks:
name: Gitleaks
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
timeout-minutes: 20
steps:
- name: Checkout
uses: actions/checkout@v7
uses: actions/checkout@v6
with:
fetch-depth: 0
persist-credentials: false
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/summary.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ concurrency:

jobs:
summary:
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
timeout-minutes: 10
permissions:
issues: write
Expand Down Expand Up @@ -49,7 +49,7 @@ jobs:

- name: Comment with AI summary
if: github.event.issue.state == 'open'
uses: peter-evans/create-or-update-comment@v6
uses: peter-evans/create-or-update-comment@v5
with:
token: ${{ secrets.GITHUB_TOKEN }}
issue-number: ${{ github.event.issue.number }}
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,11 @@
"test:e2e:accessibility": "node scripts/run-playwright.mjs tests/ui-accessibility.spec.ts --project=chromium",
"test:e2e:chromium": "node scripts/run-playwright.mjs --project=chromium",
"test:e2e:visual": "node scripts/run-playwright.mjs --config=playwright.visual.config.ts",
"verify:cheap": "npm run check:runtime && npm run sitemap:check && npm run lint && npm run typecheck && npm run test",
"verify:cheap": "npm run check:runtime && npm run check:github-actions && npm run sitemap:check && npm run lint && npm run typecheck && npm run test",
"verify:ui": "npm run check:runtime && npm run test:e2e:chromium",
"verify:release": "npm run check:runtime && npm run lint && npm run typecheck && npm run test && npm run build && npm run test:e2e && npm run check:production-readiness && npm run governance:release && npm run eval:quality:release",
"ci:env-check": "node scripts/check-ci-env.mjs",
"check:github-actions": "node scripts/check-github-action-pins.mjs",
"sitemap:update": "tsx scripts/generate-site-map.ts",
"sitemap:check": "tsx scripts/generate-site-map.ts --check",
"check:runtime": "tsx scripts/check-runtime.ts",
Expand Down
14 changes: 12 additions & 2 deletions scripts/check-codex-autofix-workflow.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ const workflowPath = ".github/workflows/codex-autofix-review-comments.yml";
const workflow = fs.readFileSync(workflowPath, "utf8");

const failures = [];
const scopedResolveCommand = "@codex resolve actionable Codex review findings for this pull request and current head";
const scopedResolvePrompt = `${scopedResolveCommand} using the repository instructions. Always fix P0 and P1 findings. For P2 and lower findings, decide whether each is worth fixing automatically. Fix clear, scoped, low-risk issues with the best minimal change; otherwise reply explaining why the issue is deferred or not actionable. Do not update the branch from main, address unrelated reviews, broaden scope, or create more than one scoped fix commit unless explicitly asked. After each fix or decision, resolve the review conversation if supported. Do not use external APIs, paid services, credentials, dependency changes, or broad refactors unless explicitly authorized. Add targeted tests where behavior changes and run the narrowest relevant validation.`;

const forbiddenPatterns = [
{
Expand All @@ -22,6 +24,10 @@ const forbiddenPatterns = [
pattern: /^\s*pull_request_review:/m,
message: "Do not trigger Codex auto-resolve from whole pull_request_review events.",
},
{
pattern: /@codex resolve all review comments/,
message: "Do not use the broad Codex resolve-all command; use the scoped actionable-findings command.",
},
];

for (const { pattern, message } of forbiddenPatterns) {
Expand All @@ -38,8 +44,12 @@ if (!workflow.includes("codex-autoresolve-pr:${pr.number}")) {
failures.push("Codex auto-resolve marker must be scoped to the pull request, not the head SHA.");
}

if (!workflow.includes('sourceBody.includes("@codex resolve all review comments")')) {
failures.push("Codex auto-resolve workflow must skip comments that already look like resolve requests.");
if (!workflow.includes(`sourceBody.includes("${scopedResolveCommand}")`)) {
failures.push("Codex auto-resolve workflow must skip comments that already look like scoped resolve requests.");
}

if (!workflow.includes(`"${scopedResolvePrompt}"`)) {
failures.push("Codex auto-resolve workflow must emit the scoped actionable-findings resolve prompt.");
}

if (failures.length > 0) {
Expand Down
65 changes: 65 additions & 0 deletions scripts/check-github-action-pins.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { readdirSync, readFileSync } from "node:fs";
import path from "node:path";

const workflowDir = path.join(process.cwd(), ".github", "workflows");

const supportedMajorRanges = new Map([
[
"actions/checkout",
{
min: 4,
max: 6,
reason:
"v6 is the currently supported and documented major that this repo has vetted; upgrading to v7 should be a deliberate, reviewed decision.",
},
],
Comment thread
BigSimmo marked this conversation as resolved.
[
"peter-evans/create-or-update-comment",
{
min: 5,
max: 5,
reason: "create-or-update-comment v6 is not a documented published major for this workflow.",
},
],
]);

const usesPattern = /^\s*uses:\s*([^@\s]+)@v(\d+)\s*(?:#.*)?$/;
const runsOnLatestPattern = /^\s*runs-on:\s*ubuntu-latest\s*(?:#.*)?$/;
const failures = [];

for (const fileName of readdirSync(workflowDir)
.filter((name) => /\.ya?ml$/i.test(name))
.sort()) {
const filePath = path.join(workflowDir, fileName);
const lines = readFileSync(filePath, "utf8").split(/\r?\n/);

lines.forEach((line, index) => {
if (runsOnLatestPattern.test(line)) {
failures.push(
`${fileName}:${index + 1}: runs-on uses ubuntu-latest. Pin GitHub-hosted Linux jobs to ubuntu-24.04 so CI is not tied to the moving ubuntu-latest alias.`,
);
}

const match = line.match(usesPattern);
if (!match) return;

const [, action, rawMajor] = match;
const range = supportedMajorRanges.get(action);
if (!range) return;

const major = Number(rawMajor);
if (!Number.isInteger(major) || major < range.min || major > range.max) {
failures.push(
`${fileName}:${index + 1}: ${action}@v${rawMajor} is outside supported range v${range.min}-v${range.max}. ${range.reason}`,
);
}
});
}

if (failures.length > 0) {
console.error("GitHub Actions pin check failed:");
for (const failure of failures) console.error(`- ${failure}`);
process.exit(1);
}

console.log("GitHub Actions pin check passed.");
Loading