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
102 changes: 100 additions & 2 deletions .github/workflows/monthly_publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ jobs:
FIRESTORE_DOCUMENT: ${{ vars.FIRESTORE_DOCUMENT || 'CRYPTO_LEADER_ROTATION_LIVE_POOL' }}
GCP_PROJECT_ID: ${{ vars.GCP_PROJECT_ID }}
GCS_BUCKET: ${{ vars.GCS_BUCKET }}
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"
SELFHOSTED_CODEX_REVIEW_ENABLED: ${{ vars.SELFHOSTED_CODEX_REVIEW_ENABLED || 'true' }}
SELFHOSTED_CODEX_REVIEW_REPOSITORY: ${{ vars.SELFHOSTED_CODEX_REVIEW_REPOSITORY || 'QuantStrategyLab/SelfHostedCodexAuditBridge' }}
SELFHOSTED_CODEX_REVIEW_MODE: ${{ vars.SELFHOSTED_CODEX_REVIEW_MODE || 'review_and_fix' }}
SELFHOSTED_CODEX_REVIEW_AUTO_MERGE: ${{ vars.SELFHOSTED_CODEX_REVIEW_AUTO_MERGE || 'false' }}
LEGACY_AI_REVIEW_ENABLED: ${{ vars.LEGACY_AI_REVIEW_ENABLED || 'false' }}

steps:
- name: Checkout
Expand Down Expand Up @@ -166,8 +172,100 @@ jobs:
print(f"issue_url={issue['html_url']}", file=output)
PY

- name: Trigger AI Monthly Review
if: success() && env.PUBLISH_ENABLED != 'false'
- name: Detect Self-hosted Review GitHub App Credentials
id: codex_review_app_credentials
if: success() && env.PUBLISH_ENABLED != 'false' && env.SELFHOSTED_CODEX_REVIEW_ENABLED != 'false'
env:
APP_ID: ${{ vars.CROSS_REPO_GITHUB_APP_ID }}
APP_PRIVATE_KEY: ${{ secrets.CROSS_REPO_GITHUB_APP_PRIVATE_KEY }}
run: |
set -euo pipefail
if [ -n "${APP_ID:-}" ] && [ -n "${APP_PRIVATE_KEY:-}" ]; then
echo "available=true" >> "$GITHUB_OUTPUT"
else
echo "available=false" >> "$GITHUB_OUTPUT"
fi

- name: Create GitHub App Token For Self-hosted Review
id: codex_review_app_token
if: steps.codex_review_app_credentials.outputs.available == 'true'
continue-on-error: true
uses: actions/create-github-app-token@v3
with:
app-id: ${{ vars.CROSS_REPO_GITHUB_APP_ID }}
private-key: ${{ secrets.CROSS_REPO_GITHUB_APP_PRIVATE_KEY }}
owner: ${{ github.repository_owner }}
repositories: |
SelfHostedCodexAuditBridge
Comment on lines +198 to +199

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Use configured target repo when minting GitHub App token

The workflow exposes SELFHOSTED_CODEX_REVIEW_REPOSITORY as configurable, but the app-token step is still hardcoded to SelfHostedCodexAuditBridge. If the target repository variable is changed, the generated installation token remains scoped to the hardcoded repo and the subsequent dispatch to the configured repo can fail with authorization/not-found errors, making the configuration option partially ineffective unless a separate fallback token is supplied.

Useful? React with 👍 / 👎.

permission-contents: write

- name: Trigger Self-hosted Codex Monthly Review
if: success() && env.PUBLISH_ENABLED != 'false' && env.SELFHOSTED_CODEX_REVIEW_ENABLED != 'false'
env:
APP_TOKEN: ${{ steps.codex_review_app_token.outputs.token }}
CODEX_AUDIT_DISPATCH_TOKEN: ${{ secrets.CODEX_AUDIT_DISPATCH_TOKEN }}
Comment on lines +203 to +206

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Skip self-hosted dispatch when no token source is configured

This step runs whenever SELFHOSTED_CODEX_REVIEW_ENABLED is not 'false' (default is 'true'), but it hard-fails if neither APP_TOKEN nor CODEX_AUDIT_DISPATCH_TOKEN is present. In repositories/environments that have not added the new cross-repo app secret or dispatch token yet, monthly publish will now fail after creating the issue, which is a regression from the previous in-repo dispatch path. The dispatch step should be gated on credential availability (or self-hosted should default to disabled) so publish is not blocked by missing review-bridge secrets.

Useful? React with 👍 / 👎.

GITHUB_REPOSITORY: ${{ github.repository }}
GITHUB_REF_NAME: ${{ github.ref_name }}
ISSUE_NUMBER: ${{ steps.review_issue.outputs.issue_number }}
ISSUE_URL: ${{ steps.review_issue.outputs.issue_url }}
TARGET_REPOSITORY: ${{ env.SELFHOSTED_CODEX_REVIEW_REPOSITORY }}
REVIEW_MODE: ${{ env.SELFHOSTED_CODEX_REVIEW_MODE }}
AUTO_MERGE: ${{ env.SELFHOSTED_CODEX_REVIEW_AUTO_MERGE }}
run: |
set -euo pipefail
python - <<'PY'
import json
import os
import re
import urllib.request

token = os.environ.get("APP_TOKEN", "").strip() or os.environ.get("CODEX_AUDIT_DISPATCH_TOKEN", "").strip()
if not token:
raise RuntimeError(
"Self-hosted Codex review dispatch requires either a GitHub App token "
"or CODEX_AUDIT_DISPATCH_TOKEN"
)
target_repository = os.environ["TARGET_REPOSITORY"].strip()
if not re.fullmatch(r"[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+", target_repository):
raise RuntimeError(f"Invalid self-hosted review repository: {target_repository!r}")
mode = os.environ["REVIEW_MODE"].strip() or "review_and_fix"
if mode not in {"review_only", "review_and_fix"}:
raise RuntimeError(f"Unsupported self-hosted review mode: {mode}")

payload = {
"event_type": "monthly-review-created",
"client_payload": {
"source_repo": os.environ["GITHUB_REPOSITORY"],
"source_ref": os.environ["GITHUB_REF_NAME"],
"issue_number": os.environ["ISSUE_NUMBER"],
"issue_url": os.environ["ISSUE_URL"],
"mode": mode,
"auto_merge": os.environ["AUTO_MERGE"].strip().lower() == "true",
},
}
request = urllib.request.Request(
f"https://api.github.com/repos/{target_repository}/dispatches",
data=json.dumps(payload).encode("utf-8"),
method="POST",
headers={
"Authorization": f"Bearer {token}",
"Accept": "application/vnd.github+json",
"Content-Type": "application/json",
"X-GitHub-Api-Version": "2022-11-28",
"User-Agent": "crypto-leader-rotation-monthly-publish",
},
)
with urllib.request.urlopen(request) as response:
if response.status not in (201, 204):
raise RuntimeError(f"Unexpected dispatch status: {response.status}")
print(
f"Dispatched self-hosted Codex review for issue #{os.environ['ISSUE_NUMBER']} "
f"to {target_repository}"
)
PY

- name: Trigger Legacy AI Monthly Review
if: success() && env.PUBLISH_ENABLED != 'false' && env.LEGACY_AI_REVIEW_ENABLED == 'true'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_REPOSITORY: ${{ github.repository }}
Expand Down
12 changes: 12 additions & 0 deletions tests/test_monthly_publish_workflow_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,18 @@ def test_monthly_review_issue_creation_does_not_require_gh_cli(self) -> None:
self.assertIn("https://api.github.com/repos/{repository}", workflow)
self.assertIn('GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}', workflow)
self.assertIn("issue_number=", workflow)
self.assertIn("SELFHOSTED_CODEX_REVIEW_REPOSITORY", workflow)
self.assertIn("QuantStrategyLab/SelfHostedCodexAuditBridge", workflow)
self.assertIn("CROSS_REPO_GITHUB_APP_ID", workflow)
self.assertIn("CROSS_REPO_GITHUB_APP_PRIVATE_KEY", workflow)
self.assertIn("actions/create-github-app-token@v3", workflow)
self.assertIn("SelfHostedCodexAuditBridge", workflow)
self.assertIn("permission-contents: write", workflow)
self.assertIn("APP_TOKEN", workflow)
self.assertIn("CODEX_AUDIT_DISPATCH_TOKEN", workflow)
self.assertIn("monthly-review-created", workflow)
self.assertIn("/repos/{target_repository}/dispatches", workflow)
self.assertIn("LEGACY_AI_REVIEW_ENABLED == 'true'", workflow)
self.assertIn("/actions/workflows/ai_review.yml/dispatches", workflow)

def test_ai_review_workflow_supports_dispatch_and_comment_posting(self) -> None:
Expand Down