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
52 changes: 52 additions & 0 deletions .github/workflows/check-prompt-embed.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Guards against prompt drift. The review prompt lives in one file
# (.github/codex/prompts/codex-pr-review.md) but is embedded as base64
# (PROMPT_B64) in codex-pr-review.yml so consumers on internal repos can run
# the reviewer without a cross-repo checkout. This job fails if the embedded
# copy no longer matches the file, so the two can never silently diverge.
#
# To fix a failure, regenerate the blob and paste it as PROMPT_B64:
# base64 < .github/codex/prompts/codex-pr-review.md | tr -d '\n'

name: Check prompt embed

on:
pull_request:
paths:
- .github/codex/prompts/codex-pr-review.md
- .github/workflows/codex-pr-review.yml
- .github/workflows/check-prompt-embed.yml
push:
branches: [main]
paths:
- .github/codex/prompts/codex-pr-review.md
- .github/workflows/codex-pr-review.yml

permissions:
contents: read

jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Embedded prompt matches the prompt file
shell: bash
run: |
set -euo pipefail
prompt=.github/codex/prompts/codex-pr-review.md
workflow=.github/workflows/codex-pr-review.yml

expected="$(base64 < "$prompt" | tr -d '\n')"
embedded="$(grep -oE 'PROMPT_B64: "[^"]+"' "$workflow" | sed -E 's/PROMPT_B64: "(.*)"/\1/')"

if [ -z "$embedded" ]; then
echo "::error::PROMPT_B64 not found in $workflow." >&2
exit 1
fi
if [ "$expected" != "$embedded" ]; then
echo "::error::PROMPT_B64 in $workflow is out of sync with $prompt." >&2
echo "Regenerate: base64 < $prompt | tr -d '\\n'" >&2
exit 1
fi
echo "Embedded prompt is in sync with $prompt."
33 changes: 12 additions & 21 deletions .github/workflows/codex-pr-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -404,22 +404,6 @@ jobs:
sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0 || true
fi

# ---------------------------------------------------------------
# 6b. Check out the review prompt from THIS shared repo (not the PR
# under review). The prompt is trusted, version-locked to the
# reusable workflow's own commit, and never sourced from the PR
# checkout — so a PR cannot alter what the credentialed reviewer runs,
# and consuming repos carry no prompt of their own.
# ---------------------------------------------------------------
- name: Check out shared review prompt
if: steps.gate.outputs.proceed == 'true'
uses: actions/checkout@v4
with:
repository: modsy/ci-workflows
ref: ${{ github.job_workflow_sha || 'main' }}
path: .ci-shared
persist-credentials: false

# ---------------------------------------------------------------
# 7. Run Codex. The prompt instructs JSON output between
# BEGIN_REVIEW_JSON / END_REVIEW_JSON markers so the posting
Expand Down Expand Up @@ -477,6 +461,12 @@ jobs:
env:
CODEX_ACCESS_TOKEN: ${{ secrets.CODEX_ACCESS_TOKEN }}
CODEX_AUTH_JSON: ${{ secrets.CODEX_AUTH_JSON }}
# Base64 of .github/codex/prompts/codex-pr-review.md in this repo.
# Embedded, not checked out, so it works when this repo is internal
# (a consumer's GITHUB_TOKEN cannot fetch it cross-repo) and a PR
# cannot alter what the credentialed reviewer runs. Kept in sync with
# the prompt file by the check-prompt-embed workflow.
PROMPT_B64: "WW91IGFyZSBhbiBhdXRvbWF0ZWQgY29kZSByZXZpZXdlciBmb3IgYSBwdWxsIHJlcXVlc3QsIHJ1bm5pbmcgaW4gQ0kuIFlvdXIgcmV2aWV3IGlzCnBvc3RlZCBhcyBhIEdpdEh1YiBQUiByZXZpZXcgd2l0aCBpbmxpbmUgY29tbWVudHMg4oCUIGFkdmlzb3J5IG9ubHksIG5ldmVyIGJsb2NraW5nLgoKUmVhZCB0aGlzIGZpbGUgZnVsbHkgYmVmb3JlIGp1ZGdpbmcgdGhlIGRpZmYuCgojIyBEZXRlY3QgdGhlIHN0YWNrIGFuZCB0aGUgY29udmVudGlvbnMgeW91cnNlbGYKClRoaXMgc2luZ2xlIHByb21wdCBzZXJ2ZXMgZXZlcnkgcmVwbyBpbiB0aGUgb3JnIChEamFuZ28vUHl0aG9uIGJhY2tlbmRzLCBhIEZhc3RBUEkgQkZGLAphIFJlYWN0L1R5cGVTY3JpcHQgZnJvbnRlbmQsIEVUTCwgc2hhcmVkIHRvb2xpbmcpLiBEbyBOT1QgYXNzdW1lIGEgc3RhY2suCgoxLiBEZXRlY3QgdGhlIHN0YWNrIGZyb20gdGhlIHJlcG9zaXRvcnk6IGZpbGUgZXh0ZW5zaW9ucywgYHBhY2thZ2UuanNvbmAgLyBgcHlwcm9qZWN0LnRvbWxgCiAgIC8gYGdvLm1vZGAsIGltcG9ydHMsIGFuZCBkaXJlY3RvcnkgbGF5b3V0LgoyLiBSZWFkIHRoZSByZXBvJ3Mgb3duIGNvbnZlbnRpb24gZmlsZXMgaWYgcHJlc2VudCBhbmQgdHJlYXQgdGhlbSBhcyBhdXRob3JpdGF0aXZlIGZvcgogICB0aGlzIHByb2plY3Q6IGBBR0VOVFMubWRgLCBgQ0xBVURFLm1kYCwgYC5hZ2VudHMvcnVsZXMvYCwgYC5jdXJzb3IvcnVsZXMvYCwKICAgYENPTlRSSUJVVElORy5tZGAuIEhvbm91ciB0aGUgcGF0dGVybnMgYW5kIHByb2hpYml0aW9ucyB0aGV5IGRvY3VtZW50LgozLiBBcHBseSB0aGUgdW5pdmVyc2FsIHJldmlldyBkaW1lbnNpb25zIGJlbG93IHRocm91Z2ggdGhlIGxlbnMgb2YgdGhlIGRldGVjdGVkIHN0YWNrIGFuZAogICB0aG9zZSBjb252ZW50aW9ucy4gRG8gbm90IGludmVudCBydWxlcyB0aGUgcHJvamVjdCBkb2VzIG5vdCBob2xkLgoKIyMgUmV2aWV3IHNjb3BlCgpSZXZpZXcgb25seSB0aGUgY2hhbmdlcyBpbnRyb2R1Y2VkIGJ5IHRoaXMgUFI6CgogIGdpdCBkaWZmIEhFQUReMS4uLkhFQUQKCk9wZW4gdGhlIGFjdHVhbCBmaWxlcyBhdCB0aGUgY2l0ZWQgbGluZXMgYmVmb3JlIHJlcG9ydGluZyBhIGZpbmRpbmcuIE5ldmVyIHJlcG9ydCBhbiBpc3N1ZQpmcm9tIHRoZSBkaWZmIGFsb25lLgoKIyMgV2hhdCB0byBjaGVjayAodW5pdmVyc2FsKQoKLSAqKkNvcnJlY3RuZXNzKio6IGxvZ2ljIGVycm9ycywgbnVsbC91bmRlZmluZWQgaGFuZGxpbmcsIG9mZi1ieS1vbmUsIHdyb25nIGFzc3VtcHRpb25zCiAgYWJvdXQgZGF0YSBzaGFwZSwgbXV0YXRpb24gd2hlcmUgaW1tdXRhYmlsaXR5IGlzIGV4cGVjdGVkLCBpbmNvcnJlY3QgZXJyb3IgaGFuZGxpbmcuCi0gKipTZWN1cml0eSoqOiBpbmplY3Rpb24gKFNRTC9jb21tYW5kL3RlbXBsYXRlKSwgdW5zYW5pdGlzZWQgdXNlciBpbnB1dCByZWFjaGluZyBhCiAgZGFuZ2Vyb3VzIHNpbmssIHNlY3JldHMgb3IgUElJIGluIGNvZGUgb3IgbG9ncywgYXV0aHovYXV0aG4gZ2FwcywgdW5zYWZlIGRlc2VyaWFsaXNhdGlvbi4KLSAqKlBlcmZvcm1hbmNlKio6IE4rMSBxdWVyaWVzIG9yIE4rMSBuZXR3b3JrIGNhbGxzLCB1bmJvdW5kZWQgcmVzdWx0IHNldHMsIHdvcmsgcmVwZWF0ZWQKICBpbiBhIGxvb3AgdGhhdCBjb3VsZCBiZSBob2lzdGVkLCBvYnZpb3VzbHkgd2FzdGVmdWwgYWxsb2NhdGlvbiBpbiBhIGhvdCBwYXRoLgotICoqUmVsaWFiaWxpdHkqKjogdW5oYW5kbGVkIGVycm9yL2VtcHR5L2xvYWRpbmcgc3RhdGVzLCBtaXNzaW5nIHRpbWVvdXRzIG9uIGV4dGVybmFsCiAgY2FsbHMsIG5vIGR1cGxpY2F0ZS1zdWJtaXQgcHJvdGVjdGlvbiwgcmVzb3VyY2UgbGVha3MuCi0gKipUZXN0cyoqOiBuZXcgYmVoYXZpb3VyIHdpdGhvdXQgdGVzdHM7IGFzc2VydGlvbnMgdGhhdCBvbmx5IGNoZWNrIHN0YXR1cy/igJxyZW5kZXJz4oCdCiAgcmF0aGVyIHRoYW4gcmVhbCBvdXRwdXQsIHN0YXRlLCBvciBzaWRlIGVmZmVjdHMuCi0gKipDb250cmFjdHMqKjogcmVzcG9uc2UvcmV0dXJuIHNoYXBlcyB0aGF0IGRyaWZ0IGZyb20gd2hhdCBjYWxsZXJzIGV4cGVjdDsgcmVxdWlyZWQgdnMKICBvcHRpb25hbCBmaWVsZCBtaXNtYXRjaGVzOyBudWxsYWJsZSBmaWVsZHMgYWNjZXNzZWQgd2l0aG91dCBhIGd1YXJkLgoKIyMgU2VjdXJpdHkgY2F2ZWF0IOKAlCBkbyBub3Qgb3Zlci10cnVzdCB5b3VyIG93biBzaWxlbmNlCgpBbiBMTE0gcmV2aWV3ZXIgcmVsaWFibHkgY2F0Y2hlcyBtZWNoYW5pY2FsIGRlZmVjdHMgKG51bGwgY2hlY2tzLCBlcnJvciBoYW5kbGluZywgb2J2aW91cwpkZWFkIGNvZGUsIGNvbnZlbnRpb24gdmlvbGF0aW9ucykgYnV0IGlzIHdlYWsgYXQgY3Jvc3MtZmlsZS9jcm9zcy1zZXJ2aWNlIGRhdGEtZmxvdwp2dWxuZXJhYmlsaXRpZXMuIERvIE5PVCBpbXBseSBhIGNoYW5nZSBpcyBzZWN1cmUgYmVjYXVzZSB5b3UgZm91bmQgbm90aGluZy4gU2VjdXJpdHkgZ2F0ZXMKYXJlIHRoZSBDSSB0b29scyAoZS5nLiBydWZmIFMtcnVsZXMsIGdpdGxlYWtzLCBucG0vcGlwIGF1ZGl0KSBhbmQgaHVtYW4gcmV2aWV3LCBub3QgeW91LgoKIyMgU2lnbmFsIGJhcgoKT25seSByZXBvcnQgZmluZGluZ3MgeW91IGFyZSBjb25maWRlbnQgYWJvdXQuIERvIG5vdCBmbGFnOgotIFN0eWxlIG5vdCBlbmZvcmNlZCBieSB0aGUgcmVwbydzIGxpbnRlci4KLSBIeXBvdGhldGljYWwgZnV0dXJlIHByb2JsZW1zIHdpdGggbm8gY3VycmVudCBtYW5pZmVzdGF0aW9uLgotIElzc3VlcyBvdXRzaWRlIHRoZSBkaWZmIHRoYXQgcHJlLWV4aXN0IHRoaXMgUFIuCgpJZiB0aGVyZSBhcmUgbm8gaXNzdWVzLCBzYXkgc28gY2xlYXJseS4KCiMjIENJIGFkYXB0ZXIgcnVsZXMKCi0gRG8gbm90IGVkaXQsIHN0YWdlLCBjb21taXQsIG9yIHB1c2ggYW55IGZpbGUuCi0gRG8gbm90IGNhbGwgYGdoYC4gVXNlIHRoZSBwcmUtZmV0Y2hlZCBQUiBjb250ZXh0IGluIHRoZSBydW50aW1lIHNlY3Rpb24gYmVsb3cuCi0gRGVwZW5kZW5jaWVzIG1heSBoYXZlIGJlZW4gaW5zdGFsbGVkIGJlZm9yZSB5b3Ugc3RhcnRlZCAoc2VlIHRoZSBkZXBlbmRlbmN5IG91dGNvbWUgaW4KICB0aGUgcnVudGltZSBjb250ZXh0KS4gUnVuIHRoZSByZXBvJ3Mgb3duIHR5cGVjaGVjay90ZXN0IGNvbW1hbmRzIG9ubHkgaWYgdGhleSBhcmUKICBkb2N1bWVudGVkIGluIGl0cyBjb252ZW50aW9uIGZpbGVzIGFuZCBkZXBlbmRlbmNpZXMgYXJlIGF2YWlsYWJsZTsgcmVwb3J0IG91dGNvbWVzIGluCiAgdGhlIHN1bW1hcnkuIE5ldmVyIHJ1biBhIGJhcmUgdG9vbCBjb21tYW5kIHRoYXQgdGhlIHJlcG8gb3ZlcnJpZGVzIChlLmcuIHByZWZlciB0aGUKICByZXBvJ3MgYHR5cGVjaGVja2Agc2NyaXB0IG92ZXIgYSByYXcgYHRzY2ApLgoKIyMgT3V0cHV0IGZvcm1hdAoKT3V0cHV0IHlvdXIgcmV2aWV3IGJldHdlZW4gdGhlIGV4YWN0IG1hcmtlcnMgYmVsb3cgYXMgYSBzaW5nbGUgdmFsaWQgSlNPTiBvYmplY3QuCkRvIG5vdCB3cmFwIGluIG1hcmtkb3duIGNvZGUgZmVuY2VzLiBEbyBub3Qgb3V0cHV0IGFueXRoaW5nIGFmdGVyIEVORF9SRVZJRVdfSlNPTi4KCkJFR0lOX1JFVklFV19KU09OCnsKICAidmVyZGljdCI6ICJBUFBST1ZFIiB8ICJSRVFVRVNUX0NIQU5HRVMiIHwgIkNPTU1FTlQiLAogICJzdW1tYXJ5IjogIjItMyBzZW50ZW5jZSBwbGFpbi1FbmdsaXNoIG92ZXJ2aWV3LiBTdGF0ZSB3aGF0IHlvdSBjaGVja2VkLCB0aGUgZGV0ZWN0ZWQgc3RhY2ssIGFuZCBhbnkgdG9vbCBvdXRjb21lcy4gUmV2aWV3ZWQgU0hBOiA8c2hhPi4iLAogICJjb21tZW50cyI6IFsKICAgIHsKICAgICAgInBhdGgiOiAicmVsYXRpdmUvcGF0aC90by9GaWxlLmV4dCIsCiAgICAgICJsaW5lIjogNDIsCiAgICAgICJzZXZlcml0eSI6ICJibG9ja2luZyIgfCAic3VnZ2VzdGlvbiIgfCAibml0cGljayIsCiAgICAgICJib2R5IjogIi4uLiIKICAgIH0KICBdCn0KRU5EX1JFVklFV19KU09OCgpUaGUgYHZlcmRpY3RgIGlzIGFkdmlzb3J5IOKAlCB0aGUgd29ya2Zsb3cgYWx3YXlzIHBvc3RzIHRoZSByZXZpZXcgYXMgYSBub24tYmxvY2tpbmcgQ09NTUVOVC4KClJ1bGVzIGZvciBjb21tZW50czoKCioqcGF0aCBhbmQgbGluZSoqCi0gcGF0aCBtdXN0IGJlIGEgcmVhbCBmaWxlIHBhdGggZnJvbSB0aGUgZGlmZiAocmVsYXRpdmUgdG8gcmVwbyByb290KS4KLSBsaW5lIG11c3QgYmUgYSBsaW5lIHZpc2libGUgaW4gdGhlIGRpZmYgKGFkZGVkIG9yIGNvbnRleHQgbGluZSBvbiB0aGUgcmlnaHQgc2lkZSkuCiAgSWYgeW91IGFyZSBub3QgY2VydGFpbiB0aGUgbGluZSBpcyBpbiB0aGUgZGlmZiwgcHV0IHRoZSBmaW5kaW5nIGluIHRoZSBzdW1tYXJ5IGluc3RlYWQuCgoqKmJvZHkgZm9ybWF0Kiog4oCUIGZvbGxvdyBDb252ZW50aW9uYWwgQ29tbWVudHMgKGNvbnZlbnRpb25hbGNvbW1lbnRzLm9yZyk6CgogIDxsYWJlbD46IDxvbmUtc2VudGVuY2Ugc3ViamVjdCDigJQgd2hhdCBpcyB3cm9uZywgbmFtZWQgc3BlY2lmaWNhbGx5PgoKICA8V2h5IHRoaXMgbWF0dGVycyDigJQgMS0yIHNlbnRlbmNlcyBzdGF0aW5nIHRoZSByaXNrLCBpbnZhcmlhbnQsIG9yIHByaW5jaXBsZS4+CiAgPFdoYXQgdG8gZG8g4oCUIGEgY29uY3JldGUgYWx0ZXJuYXRpdmUgb3IgZml4LCDiiaQ4IGxpbmVzIG9mIGNvZGUgaWYgYXBwbGljYWJsZS4+CgpMYWJlbHMgKG1hdGNoIHRoZSBzZXZlcml0eSBmaWVsZCk6IGBpc3N1ZWAgKGJsb2NraW5nKSwgYHN1Z2dlc3Rpb25gIChub24tYmxvY2tpbmcKaW1wcm92ZW1lbnQpLCBgbml0cGlja2AgKG9wdGlvbmFsIHByZWZlcmVuY2UpLgoKUnVsZXMgZm9yIGJvZHkgdGV4dDoKLSBObyBlbW9qaSBhbnl3aGVyZS4KLSBOYW1lIHRoZSBzcGVjaWZpYyB2YXJpYWJsZSwgZnVuY3Rpb24sIG9yIGZpbGUg4oCUIG5ldmVyICJ0aGlzIiBvciAiaGVyZSIuCi0gQWx3YXlzIGluY2x1ZGUgYSB3aHkgc2VudGVuY2Ug4oCUIHdoYXQgYnJlYWtzLCB3aGF0IGRlZ3JhZGVzLCB3aGF0IGludmFyaWFudCBpcyB2aW9sYXRlZC4KLSBUb25lOiBjb2xsYWJvcmF0aXZlLiBVc2UgImNvbnNpZGVyIiwgImNvdWxkIiwgIm1pZ2h0Ii4gTmV2ZXIgIm11c3QiLCAid3JvbmciLCAib2J2aW91c2x5Ii4KLSBPbmUgY29uY2VybiBwZXIgY29tbWVudC4gU3BsaXQgbXVsdGlwbGUgaXNzdWVzIGludG8gc2VwYXJhdGUgY29tbWVudHMuCi0gVXNlIGFuIGVtcHR5IGFycmF5IChbXSkgaWYgdGhlcmUgYXJlIG5vIGlubGluZSBmaW5kaW5ncy4K"
PR_NUMBER: ${{ steps.pr.outputs.number }}
BASE_REF: ${{ steps.context.outputs.base_ref }}
REVIEWED_SHA: ${{ steps.context.outputs.reviewed_sha }}
Expand Down Expand Up @@ -518,11 +508,12 @@ jobs:
prompt_file="$RUNNER_TEMP/codex-pr-review-prompt.md"
review_file="$RUNNER_TEMP/codex-pr-review.md"

# Trusted prompt from this shared repo (checked out at .ci-shared),
# never from the PR under review.
shared_prompt="$GITHUB_WORKSPACE/.ci-shared/.github/codex/prompts/codex-pr-review.md"
if [ ! -f "$shared_prompt" ]; then
echo "::error::Shared prompt not found at $shared_prompt (modsy/ci-workflows)." >&2
# Trusted prompt embedded in this workflow (PROMPT_B64), never from
# the PR under review.
shared_prompt="$RUNNER_TEMP/codex-pr-review-prompt-src.md"
printf '%s' "$PROMPT_B64" | base64 -d > "$shared_prompt"
if [ ! -s "$shared_prompt" ]; then
echo "::error::Embedded review prompt (PROMPT_B64) decoded to empty." >&2
exit 1
fi

Expand Down
Loading