Skip to content

Commit a5a0107

Browse files
add feature parity workflow (#853)
# why To keep up with changes on client SDKs # what changed Added a workflow 'Feature Parity' that, given an added (specific) label to a PR, will open an issue on the sibling repo describing the PR for future implementation # test plan --------- Co-authored-by: Roaring <[email protected]>
1 parent 2d8485f commit a5a0107

File tree

3 files changed

+177
-0
lines changed

3 files changed

+177
-0
lines changed

.github/workflows/feature-parity.yml

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
name: Feature Parity
2+
3+
on:
4+
pull_request:
5+
types:
6+
- opened
7+
- synchronize
8+
- labeled
9+
- unlabeled
10+
paths-ignore:
11+
- "docs/**"
12+
13+
jobs:
14+
check-parity-label:
15+
runs-on: ubuntu-latest
16+
if: github.event.action == 'labeled' && github.event.label.name == 'parity'
17+
permissions:
18+
contents: read
19+
pull-requests: write
20+
issues: write
21+
steps:
22+
- name: Check out repository code
23+
uses: actions/checkout@v4
24+
25+
- name: Check user permissions
26+
uses: actions/github-script@v7
27+
with:
28+
github-token: ${{ secrets.GITHUB_TOKEN }}
29+
script: |
30+
const { data: permission } = await github.rest.repos.getCollaboratorPermissionLevel({
31+
owner: context.repo.owner,
32+
repo: context.repo.repo,
33+
username: context.actor
34+
});
35+
36+
const hasWriteAccess = ['admin', 'write'].includes(permission.permission);
37+
38+
if (!hasWriteAccess) {
39+
// Remove the parity label if user doesn't have write access
40+
await github.rest.issues.removeLabel({
41+
owner: context.repo.owner,
42+
repo: context.repo.repo,
43+
issue_number: context.issue.number,
44+
name: 'parity'
45+
});
46+
47+
// Add a comment explaining why the label was removed
48+
await github.rest.issues.createComment({
49+
owner: context.repo.owner,
50+
repo: context.repo.repo,
51+
issue_number: context.issue.number,
52+
body: `❌ **Parity Label Removed**\n\n@${context.actor}, you do not have sufficient permissions to add the 'parity' label. Only users with write access can trigger feature parity issues.\n\nIf you believe this feature should be implemented in the Python SDK, please ask a maintainer to add the label.`
53+
});
54+
55+
throw new Error(`User ${context.actor} does not have write access to add parity label`);
56+
}
57+
58+
console.log(`User ${context.actor} has ${permission.permission} access - proceeding with parity workflow`);
59+
60+
- name: Generate GitHub App token
61+
id: generate-token
62+
uses: actions/create-github-app-token@v1
63+
with:
64+
app-id: ${{ secrets.PARITY_APP_ID }}
65+
private-key: ${{ secrets.PARITY_APP_PRIVATE_KEY }}
66+
owner: browserbase
67+
repositories: stagehand
68+
69+
- name: Create issue in Python SDK repository
70+
uses: actions/github-script@v7
71+
with:
72+
github-token: ${{ steps.generate-token.outputs.token }}
73+
script: |
74+
const { data: pullRequest } = await github.rest.pulls.get({
75+
owner: context.repo.owner,
76+
repo: context.repo.repo,
77+
pull_number: context.issue.number,
78+
});
79+
80+
// Get PR comments for additional context
81+
const { data: comments } = await github.rest.issues.listComments({
82+
owner: context.repo.owner,
83+
repo: context.repo.repo,
84+
issue_number: context.issue.number,
85+
});
86+
87+
// Format comments for the issue description
88+
let commentsSection = '';
89+
if (comments.length > 0) {
90+
commentsSection = '\n\n## Recent Comments\n\n';
91+
comments.slice(-3).forEach(comment => {
92+
commentsSection += `**@${comment.user.login}** commented:\n`;
93+
commentsSection += `${comment.body.substring(0, 500)}${comment.body.length > 500 ? '...' : ''}\n\n`;
94+
});
95+
}
96+
97+
// Get list of changed files for context
98+
const { data: files } = await github.rest.pulls.listFiles({
99+
owner: context.repo.owner,
100+
repo: context.repo.repo,
101+
pull_number: context.issue.number,
102+
});
103+
104+
const changedFiles = files.map(file => `- \`${file.filename}\``).join('\n');
105+
106+
const issueTitle = `[Feature Parity] ${pullRequest.title}`;
107+
const issueBody = `## Feature Parity Request
108+
109+
This issue was automatically created from a pull request in the TypeScript Stagehand repository that was labeled with 'parity'.
110+
111+
### Original PR Details
112+
- **PR**: #${context.issue.number} - ${pullRequest.title}
113+
- **Author**: @${pullRequest.user.login}
114+
- **Link**: ${pullRequest.html_url}
115+
116+
### Description
117+
${pullRequest.body || 'No description provided.'}
118+
119+
### Changed Files
120+
${changedFiles}
121+
122+
${commentsSection}
123+
124+
### Action Required
125+
Please review the changes in the original PR and implement equivalent functionality in the Python SDK if applicable.
126+
127+
---
128+
*This issue was automatically generated by the Feature Parity workflow.*`;
129+
130+
// Create the issue in the Python repository
131+
const { data: issue } = await github.rest.issues.create({
132+
owner: 'browserbase',
133+
repo: 'stagehand-python',
134+
title: issueTitle,
135+
body: issueBody,
136+
labels: ['feature-parity']
137+
});
138+
139+
console.log(`Created issue: ${issue.html_url}`);
140+
141+
// Add a comment to the original PR confirming the issue was created
142+
await github.rest.issues.createComment({
143+
owner: context.repo.owner,
144+
repo: context.repo.repo,
145+
issue_number: context.issue.number,
146+
body: `🔄 **Feature Parity Issue Created**\n\nAn issue has been automatically created in the Python SDK repository to track parity implementation:\n${issue.html_url}`
147+
});

README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,23 @@
3232
<a href="https://trendshift.io/repositories/12122" target="_blank"><img src="https://trendshift.io/api/badge/repositories/12122" alt="browserbase%2Fstagehand | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
3333
</p>
3434

35+
<p align="center">
36+
If you're looking for the Python implementation, you can find it
37+
<a href="https://github.com/browserbase/stagehand-python"> here</a>
38+
</p>
39+
40+
<div align="center" style="display: flex; align-items: center; justify-content: center; gap: 4px; margin-bottom: 0;">
41+
<b>Vibe code</b>
42+
<span style="font-size: 1.05em;"> Stagehand with </span>
43+
<a href="https://director.ai" style="display: flex; align-items: center;">
44+
<span>Director</span>
45+
</a>
46+
<span> </span>
47+
<picture>
48+
<img alt="Director" src="media/director_icon.svg" width="25" />
49+
</picture>
50+
</div>
51+
3552
## Why Stagehand?
3653

3754
Most existing browser automation tools either require you to write low-level code in a framework like Selenium, Playwright, or Puppeteer, or use high-level agents that can be unpredictable in production. By letting developers choose what to write in code vs. natural language, Stagehand is the natural choice for browser automations in production.

media/director_icon.svg

Lines changed: 13 additions & 0 deletions
Loading

0 commit comments

Comments
 (0)