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
21 changes: 15 additions & 6 deletions .github/workflows/codex-code-review.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Generated by python3 ocr/render_workflow.py; edit ocr/ci_client.py.
name: OCR Code Review
name: OCR Dispatch

on:
pull_request_target:
Expand Down Expand Up @@ -188,10 +188,16 @@ jobs:


def summary(identity, reply):
text = (f"## OCR dispatch accepted\n\nRepository: `{identity['repository']}`; PR #{identity['pull_number']}\n\n"
skipped = reply.get('state') == 'skipped' and reply.get('reason') == 'repository_not_enabled'
heading = 'OCR dispatch skipped' if skipped else 'OCR dispatch accepted'
if skipped:
details = 'Automatic review is disabled for this repository. Dispatch skipped by repository policy.\n'
else:
details = ('Task persisted. The backend owns dispatch, recovery and result publication.\n'
'The separate ocr-review status reports the review verdict; enforcement is configured in Rulesets.\n')
text = (f"## {heading}\n\nRepository: `{identity['repository']}`; PR #{identity['pull_number']}\n\n"
f"Head: `{identity['head_sha']}`\n\n"
'Task persisted. The backend owns dispatch, recovery and result publication.\n'
'The separate ocr-review status reports the review verdict; enforcement is configured in Rulesets.\n')
+ details)
if os.environ.get('GITHUB_STEP_SUMMARY'):
with open(os.environ['GITHUB_STEP_SUMMARY'], 'a') as output:
output.write(text)
Expand All @@ -218,11 +224,14 @@ jobs:
result = verdict(reply)
state = reply['state']
if state != last_state:
print('OCR state: ' + state, flush=True)
print('OCR dispatch state: ' + state, flush=True)
last_state = state
if result is not None:
summary(identity, reply)
print('OCR dispatch accepted; the separate ocr-review status reports the review verdict.')
if state == 'skipped' and reply.get('reason') == 'repository_not_enabled':
print('OCR dispatch skipped: repository_not_enabled.')
else:
print('OCR dispatch accepted; the separate ocr-review status reports the review verdict.')
return result
remaining = deadline - time.monotonic()
if remaining <= 0:
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

The OCR dispatch entry point for selected public repositories in the organization. Workflow source: `.github/workflows/codex-code-review.yml` on the `main` branch.

The workflow is displayed as `OCR Dispatch`. An authenticated request for an unenrolled repository exits successfully with `repository_not_enabled` and a dispatch-skipped summary. The backend records this policy decision without starting review.

## Administrator setup

1. In the organization settings, go to **Settings → Rules → Rulesets** and create a **Branch ruleset**, for example, `OCR public repositories`.
Expand Down
19 changes: 14 additions & 5 deletions ocr/ci_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -164,10 +164,16 @@ def post(path, payload, token, deadline):


def summary(identity, reply):
text = (f"## OCR dispatch accepted\n\nRepository: `{identity['repository']}`; PR #{identity['pull_number']}\n\n"
skipped = reply.get('state') == 'skipped' and reply.get('reason') == 'repository_not_enabled'
heading = 'OCR dispatch skipped' if skipped else 'OCR dispatch accepted'
if skipped:
details = 'Automatic review is disabled for this repository. Dispatch skipped by repository policy.\n'
else:
details = ('Task persisted. The backend owns dispatch, recovery and result publication.\n'
'The separate ocr-review status reports the review verdict; enforcement is configured in Rulesets.\n')
text = (f"## {heading}\n\nRepository: `{identity['repository']}`; PR #{identity['pull_number']}\n\n"
f"Head: `{identity['head_sha']}`\n\n"
'Task persisted. The backend owns dispatch, recovery and result publication.\n'
'The separate ocr-review status reports the review verdict; enforcement is configured in Rulesets.\n')
+ details)
if os.environ.get('GITHUB_STEP_SUMMARY'):
with open(os.environ['GITHUB_STEP_SUMMARY'], 'a') as output:
output.write(text)
Expand All @@ -194,11 +200,14 @@ def main():
result = verdict(reply)
state = reply['state']
if state != last_state:
print('OCR state: ' + state, flush=True)
print('OCR dispatch state: ' + state, flush=True)
last_state = state
if result is not None:
summary(identity, reply)
print('OCR dispatch accepted; the separate ocr-review status reports the review verdict.')
if state == 'skipped' and reply.get('reason') == 'repository_not_enabled':
print('OCR dispatch skipped: repository_not_enabled.')
else:
print('OCR dispatch accepted; the separate ocr-review status reports the review verdict.')
return result
remaining = deadline - time.monotonic()
if remaining <= 0:
Expand Down
2 changes: 1 addition & 1 deletion ocr/render_workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

ROOT = Path(__file__).resolve().parents[1]
HEADER = '''# Generated by python3 ocr/render_workflow.py; edit ocr/ci_client.py.
name: OCR Code Review
name: OCR Dispatch

on:
pull_request_target:
Expand Down
18 changes: 18 additions & 0 deletions ocr/test_ci_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ class WorkflowTests(unittest.TestCase):
def test_embedded_workflow_matches_tested_client(self):
self.assertEqual((render_workflow.ROOT / '.github/workflows/codex-code-review.yml').read_text(),
render_workflow.rendered())
self.assertIn('\nname: OCR Dispatch\n', render_workflow.rendered())

def test_base_branch_and_drafts_are_not_filtered(self):
self.assertEqual(client.coordinates(EVENT, ENV), IDENTITY)
Expand Down Expand Up @@ -115,6 +116,23 @@ def test_gateway_401_is_not_retried(self):
with self.assertRaises(client.ReviewError): client.post('ocr-ci-submit', {}, 'test', client.time.monotonic() + 60)
self.assertEqual(opener.return_value.open.call_count, 1)

def test_unenrolled_repository_skip_ends_dispatch_without_polling(self):
with tempfile.TemporaryDirectory() as directory:
event = Path(directory) / 'event.json'; event.write_text(json.dumps(EVENT))
summary_file = Path(directory) / 'summary.md'
env = {**ENV, 'GITHUB_EVENT_PATH': str(event), 'OCR_CI_TOKEN': 'test-value',
'GITHUB_STEP_SUMMARY': str(summary_file)}
ack = reply('skipped', durable=True, contract_version=2, acceptance='accepted',
scheduler='backend', task_id='task-1', reason='repository_not_enabled')
with patch.dict(os.environ, env, clear=True), patch.object(client, 'post', return_value=ack) as post:
with patch.object(client.time, 'sleep') as sleep:
self.assertEqual(client.main(), 0)
self.assertEqual(post.call_count, 1)
self.assertEqual(post.call_args.args[0], 'ocr-ci-submit')
sleep.assert_not_called()
self.assertIn('OCR dispatch skipped', summary_file.read_text())
self.assertIn('disabled for this repository', summary_file.read_text())

def test_gateway_503_has_bounded_retries(self):
error = urllib.error.HTTPError('https://example.invalid', 503, 'Unavailable', {}, io.BytesIO())
with patch.object(client.urllib.request, 'build_opener') as opener, patch.object(client.time, 'sleep'):
Expand Down
Loading