This is a first cut at a GitHub Action that lets you define prompt
templates in `.md` files under `.github/codex/labels` that will run
Codex with the associated prompt when the label is added to a GitHub
pull request.
For example, this PR includes these files:
```
.github/codex/labels/codex-attempt.md
.github/codex/labels/codex-code-review.md
.github/codex/labels/codex-investigate-issue.md
```
And the new `.github/workflows/codex.yml` workflow declares the
following triggers:
```yaml
on:
issues:
types: [opened, labeled]
pull_request:
branches: [main]
types: [labeled]
```
as well as the following expression to gate the action:
```
jobs:
codex:
if: |
(github.event_name == 'issues' && (
(github.event.action == 'labeled' && (github.event.label.name == 'codex-attempt' || github.event.label.name == 'codex-investigate-issue'))
)) ||
(github.event_name == 'pull_request' && github.event.action == 'labeled' && github.event.label.name == 'codex-code-review')
```
Note the "actor" who added the label must have write access to the repo
for the action to take effect.
After adding a label, the action will "ack" the request by replacing the
original label (e.g., `codex-review`) with an `-in-progress` suffix
(e.g., `codex-review-in-progress`). When it is finished, it will swap
the `-in-progress` label with a `-completed` one (e.g.,
`codex-review-completed`).
Users of the action are responsible for providing an `OPENAI_API_KEY`
and making it available as a secret to the action.
81 lines
2.3 KiB
TypeScript
Executable File
81 lines
2.3 KiB
TypeScript
Executable File
#!/usr/bin/env bun
|
|
|
|
import type { Config } from "./config";
|
|
|
|
import { defaultContext, EnvContext } from "./env-context";
|
|
import { loadConfig } from "./load-config";
|
|
import { setGitHubActionsUser } from "./git-user";
|
|
import { onLabeled } from "./process-label";
|
|
import { ensureBaseAndHeadCommitsForPRAreAvailable } from "./prompt-template";
|
|
import { performAdditionalValidation } from "./verify-inputs";
|
|
import { onComment } from "./comment";
|
|
import { onReview } from "./review";
|
|
|
|
async function main(): Promise<void> {
|
|
const ctx: EnvContext = defaultContext;
|
|
|
|
// Build the configuration dynamically by scanning `.github/codex/labels`.
|
|
const GITHUB_WORKSPACE = ctx.get("GITHUB_WORKSPACE");
|
|
const config: Config = loadConfig(GITHUB_WORKSPACE);
|
|
|
|
// Optionally perform additional validation of prompt template files.
|
|
performAdditionalValidation(config, GITHUB_WORKSPACE);
|
|
|
|
const GITHUB_EVENT_NAME = ctx.get("GITHUB_EVENT_NAME");
|
|
const GITHUB_EVENT_ACTION = ctx.get("GITHUB_EVENT_ACTION");
|
|
|
|
// Set user.name and user.email to a bot before Codex runs, just in case it
|
|
// creates a commit.
|
|
setGitHubActionsUser();
|
|
|
|
switch (GITHUB_EVENT_NAME) {
|
|
case "issues": {
|
|
if (GITHUB_EVENT_ACTION === "labeled") {
|
|
await onLabeled(config, ctx);
|
|
return;
|
|
} else if (GITHUB_EVENT_ACTION === "opened") {
|
|
await onComment(ctx);
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
case "issue_comment": {
|
|
if (GITHUB_EVENT_ACTION === "created") {
|
|
await onComment(ctx);
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
case "pull_request": {
|
|
if (GITHUB_EVENT_ACTION === "labeled") {
|
|
await ensureBaseAndHeadCommitsForPRAreAvailable(ctx);
|
|
await onLabeled(config, ctx);
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
case "pull_request_review": {
|
|
await ensureBaseAndHeadCommitsForPRAreAvailable(ctx);
|
|
if (GITHUB_EVENT_ACTION === "submitted") {
|
|
await onReview(ctx);
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
case "pull_request_review_comment": {
|
|
await ensureBaseAndHeadCommitsForPRAreAvailable(ctx);
|
|
if (GITHUB_EVENT_ACTION === "created") {
|
|
await onComment(ctx);
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
console.warn(
|
|
`Unsupported action '${GITHUB_EVENT_ACTION}' for event '${GITHUB_EVENT_NAME}'.`,
|
|
);
|
|
}
|
|
|
|
main();
|