Compare commits

...

2 Commits

Author SHA1 Message Date
Boris Cherny
91a8d6c8d8 docs: add push event documentation and tests
Address code review feedback:
- Add push event to supported events list in docs/custom-automations.md
- Add auto-rebase example workflow using push events
- Add tests for isPushEvent type guard
- Add tests for push event mode detection
- Add test to verify track_progress throws error for push events

🤖 Generated with [Claude Code](https://claude.ai/code)
2026-01-01 13:19:30 -08:00
Boris Cherny
8151408b90 feat: add push event support for automation workflows
Add support for `push` events as an automation event type, enabling
workflows triggered by pushes to use claude-code-action.

Changes:
- Add PushEvent to automation event types in context.ts
- Add case handler for push events in parseGitHubContext()
- Add isPushEvent() type guard
- Add test for push event mode detection

This enables use cases like auto-rebasing PRs when main is updated,
where a workflow triggers on push to main and uses a prompt to
instruct Claude to merge main into stale PR branches.
2026-01-01 13:05:06 -08:00
4 changed files with 129 additions and 2 deletions

View File

@@ -21,6 +21,7 @@ This action supports the following GitHub events ([learn more GitHub event trigg
- `issues` - When issues are opened or assigned - `issues` - When issues are opened or assigned
- `pull_request_review` - When PR reviews are submitted - `pull_request_review` - When PR reviews are submitted
- `pull_request_review_comment` - When comments are made on PR reviews - `pull_request_review_comment` - When comments are made on PR reviews
- `push` - When commits are pushed to a branch
- `repository_dispatch` - Custom events triggered via API - `repository_dispatch` - Custom events triggered via API
- `workflow_dispatch` - Manual workflow triggers (coming soon) - `workflow_dispatch` - Manual workflow triggers (coming soon)
@@ -120,3 +121,42 @@ For more control over Claude's behavior, use the `claude_args` input to pass CLI
``` ```
This provides full access to Claude Code CLI capabilities while maintaining the simplified action interface. This provides full access to Claude Code CLI capabilities while maintaining the simplified action interface.
## Auto-Rebase PRs on Push
Automatically keep PRs up to date when the main branch is updated:
```yaml
name: Auto-Rebase PRs
on:
push:
branches: [main]
permissions:
contents: write
pull-requests: write
id-token: write
jobs:
rebase-prs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: anthropics/claude-code-action@v1
with:
prompt: |
Find all open PRs that are behind main and merge main into them.
For each PR:
1. Check out the PR branch
2. Merge main into the branch
3. Push the updated branch
Skip any PRs with merge conflicts - just report them.
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
```
This workflow triggers whenever commits are pushed to main and uses Claude to automatically merge main into any stale PR branches, keeping them up to date.

View File

@@ -6,6 +6,7 @@ import type {
PullRequestEvent, PullRequestEvent,
PullRequestReviewEvent, PullRequestReviewEvent,
PullRequestReviewCommentEvent, PullRequestReviewCommentEvent,
PushEvent,
WorkflowRunEvent, WorkflowRunEvent,
} from "@octokit/webhooks-types"; } from "@octokit/webhooks-types";
import { CLAUDE_APP_BOT_ID, CLAUDE_BOT_LOGIN } from "./constants"; import { CLAUDE_APP_BOT_ID, CLAUDE_BOT_LOGIN } from "./constants";
@@ -65,6 +66,7 @@ const AUTOMATION_EVENT_NAMES = [
"repository_dispatch", "repository_dispatch",
"schedule", "schedule",
"workflow_run", "workflow_run",
"push",
] as const; ] as const;
// Derive types from constants for better maintainability // Derive types from constants for better maintainability
@@ -112,14 +114,15 @@ export type ParsedGitHubContext = BaseContext & {
isPR: boolean; isPR: boolean;
}; };
// Context for automation events (workflow_dispatch, repository_dispatch, schedule, workflow_run) // Context for automation events (workflow_dispatch, repository_dispatch, schedule, workflow_run, push)
export type AutomationContext = BaseContext & { export type AutomationContext = BaseContext & {
eventName: AutomationEventName; eventName: AutomationEventName;
payload: payload:
| WorkflowDispatchEvent | WorkflowDispatchEvent
| RepositoryDispatchEvent | RepositoryDispatchEvent
| ScheduleEvent | ScheduleEvent
| WorkflowRunEvent; | WorkflowRunEvent
| PushEvent;
}; };
// Union type for all contexts // Union type for all contexts
@@ -235,6 +238,13 @@ export function parseGitHubContext(): GitHubContext {
payload: context.payload as unknown as WorkflowRunEvent, payload: context.payload as unknown as WorkflowRunEvent,
}; };
} }
case "push": {
return {
...commonFields,
eventName: "push",
payload: context.payload as unknown as PushEvent,
};
}
default: default:
throw new Error(`Unsupported event type: ${context.eventName}`); throw new Error(`Unsupported event type: ${context.eventName}`);
} }
@@ -276,6 +286,12 @@ export function isIssuesAssignedEvent(
return isIssuesEvent(context) && context.eventAction === "assigned"; return isIssuesEvent(context) && context.eventAction === "assigned";
} }
export function isPushEvent(
context: GitHubContext,
): context is AutomationContext & { payload: PushEvent } {
return context.eventName === "push";
}
// Type guard to check if context is an entity context (has entityNumber and isPR) // Type guard to check if context is an entity context (has entityNumber and isPR)
export function isEntityContext( export function isEntityContext(
context: GitHubContext, context: GitHubContext,

View File

@@ -1,6 +1,7 @@
import { describe, expect, it } from "bun:test"; import { describe, expect, it } from "bun:test";
import { detectMode } from "../../src/modes/detector"; import { detectMode } from "../../src/modes/detector";
import type { GitHubContext } from "../../src/github/context"; import type { GitHubContext } from "../../src/github/context";
import { isPushEvent } from "../../src/github/context";
describe("detectMode with enhanced routing", () => { describe("detectMode with enhanced routing", () => {
const baseContext = { const baseContext = {
@@ -257,4 +258,65 @@ describe("detectMode with enhanced routing", () => {
expect(detectMode(context)).toBe("tag"); expect(detectMode(context)).toBe("tag");
}); });
}); });
describe("Push Events", () => {
it("should use agent mode for push events", () => {
const context: GitHubContext = {
...baseContext,
eventName: "push",
payload: {} as any,
inputs: { ...baseContext.inputs, prompt: "Merge main into stale PRs" },
};
expect(detectMode(context)).toBe("agent");
});
it("should throw error when track_progress is used with push event", () => {
const context: GitHubContext = {
...baseContext,
eventName: "push",
payload: {} as any,
inputs: { ...baseContext.inputs, trackProgress: true },
};
expect(() => detectMode(context)).toThrow(
/track_progress is only supported /,
);
});
});
describe("isPushEvent type guard", () => {
it("should return true for push events", () => {
const context: GitHubContext = {
...baseContext,
eventName: "push",
payload: {} as any,
};
expect(isPushEvent(context)).toBe(true);
});
it("should return false for non-push events", () => {
const issueContext: GitHubContext = {
...baseContext,
eventName: "issues",
eventAction: "opened",
payload: { issue: { number: 1, body: "Test" } } as any,
entityNumber: 1,
isPR: false,
};
expect(isPushEvent(issueContext)).toBe(false);
});
it("should return false for workflow_dispatch events", () => {
const context: GitHubContext = {
...baseContext,
eventName: "workflow_dispatch",
payload: {} as any,
};
expect(isPushEvent(context)).toBe(false);
});
});
}); });

View File

@@ -60,6 +60,15 @@ describe("Mode Registry", () => {
expect(mode.name).toBe("agent"); expect(mode.name).toBe("agent");
}); });
test("getMode auto-detects agent for push event", () => {
const pushContext = createMockAutomationContext({
eventName: "push",
});
const mode = getMode(pushContext);
expect(mode).toBe(agentMode);
expect(mode.name).toBe("agent");
});
test("getMode auto-detects agent for repository_dispatch with client_payload", () => { test("getMode auto-detects agent for repository_dispatch with client_payload", () => {
const contextWithPayload = createMockAutomationContext({ const contextWithPayload = createMockAutomationContext({
eventName: "repository_dispatch", eventName: "repository_dispatch",