Skip to content
Closed
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
108 changes: 19 additions & 89 deletions .agents/skills/generate-sdk-and-open-pr/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ description: Generate the Speakeasy SDK for a new version and open a release PR
allowed-tools: Bash, Read, Write, Edit, Glob, Grep
metadata:
author: youdotcom-oss
version: "1.0.0"
version: "1.1.0"
category: release
keywords: release, version, publish, pypi
---
Expand All @@ -13,49 +13,9 @@ metadata:

Release a new version of the `youdotcom` Python SDK to PyPI and GitHub.

## Step 1: Verify OpenAPI specs
The SDK is generated from remote OpenAPI specs defined in `.speakeasy/workflow.yaml`. These are merged with the overlay at `overlays/python_overlay.yaml` and output to `.speakeasy/out.openapi.yaml`. No local spec overrides are supported — if specs need changes, update them at the source URLs before running this skill.

Speakeasy generates the SDK from OpenAPI specs defined in `.speakeasy/workflow.yaml`. The current source specs are:

- `https://you.com/specs/openapi_unified_agents.yaml`
- `https://you.com/specs/openapi_search_v1.yaml`
- `https://you.com/specs/openapi_contents.yaml`
- `https://you.com/specs/openapi_base.yaml`

These are merged with the overlay at `overlays/python_overlay.yaml` and output to `.speakeasy/out.openapi.yaml`.

### 1a. Ask the user about spec sources

Use `AskUserQuestion` to ask:

```
The SDK is generated from these OpenAPI specs:

1. https://you.com/specs/openapi_unified_agents.yaml
2. https://you.com/specs/openapi_search_v1.yaml
3. https://you.com/specs/openapi_contents.yaml
4. https://you.com/specs/openapi_base.yaml

Are the updates for this release already reflected in these specs, or do you have custom specs to use?
```

Options:
- **Use existing specs** (the remote URLs already have the changes)
- **Use custom specs** (user will provide spec content or file paths)

### 1b. If custom specs

If the user provides custom specs:

1. Ask which spec(s) they want to replace and get the new content or file path
2. Update the `inputs` locations in `.speakeasy/workflow.yaml` to point to the custom spec files (e.g. change the remote URL to a local path)
3. **IMPORTANT**: Do NOT commit changes to `.speakeasy/workflow.yaml`. These are temporary overrides for generation only. Remind the user that these changes should be reverted or excluded from the release commit.

If using existing specs, move on to step 2.

## Step 2: Check current versions and fetch latest changes

Before anything else, gather the current state of the world.
## Step 1: Check current versions and fetch latest changes

### 1a. Fetch all remote changes

Expand Down Expand Up @@ -89,7 +49,7 @@ Present a summary to the user:

If any versions are out of sync, warn the user before proceeding.

## Step 3: Confirm the next version with the user
## Step 2: Confirm the next version with the user

Analyze the unreleased commits from step 1e to determine the appropriate semver bump:
- **patch** (X.Y.Z+1): bug fixes, dependency updates, docs changes only
Expand All @@ -114,70 +74,44 @@ Offer the suggested version as the recommended option, plus the other two semver

Do NOT proceed until the user confirms.

## Step 4: Generate the SDK and open a release PR
## Step 3: Generate the SDK and open a release PR

### 4a. Confirm SDK generation

Use `AskUserQuestion` to confirm:

```
Ready to run Speakeasy SDK generation for version X.Y.Z. This will regenerate the SDK source code from the OpenAPI specs.

Proceed with generation?
```

Options:
- **Yes, generate** (recommended)
- **No, cancel**

Do NOT proceed if the user cancels.

### 4b. Bump version via Speakeasy

Use `speakeasy bump` to set the version in `.speakeasy/gen.yaml`. This is the canonical way to update the Speakeasy target version.
### 3a. Bump version via Speakeasy

```bash
speakeasy bump -v X.Y.Z -t you
```

This updates `python.version` in `.speakeasy/gen.yaml` to the confirmed version.

### 4c. Run Speakeasy generation
### 3b. Run Speakeasy generation

```bash
speakeasy run
```

This will:
- Fetch the OpenAPI specs (remote URLs or local overrides from step 1)
- Fetch the OpenAPI specs from the remote URLs in `.speakeasy/workflow.yaml`
- Apply the overlay from `overlays/python_overlay.yaml`
- Regenerate all SDK source files under `src/`
- Regenerate `USAGE.md` and auto-generated sections in `README.md` (the `<!-- Start/End -->` blocks)
- Update `.speakeasy/out.openapi.yaml`

Wait for the command to complete and check for errors. If it fails, report the error to the user and stop.

### 4d. Revert temporary workflow changes

If custom specs were used in step 1, revert `.speakeasy/workflow.yaml` back to the original remote URLs:

```bash
git checkout -- .speakeasy/workflow.yaml
```

### 4e. Create a release branch
### 3c. Create a release branch

```bash
git checkout -b release/X.Y.Z
```

### 4f. Update version in all locations
### 3d. Update version in all locations

Update the version string in these files (if not already updated by Speakeasy):
- `pyproject.toml` — `version = "X.Y.Z"`
- `src/youdotcom/_version.py` — `__version__: str = "X.Y.Z"` and the `__user_agent__` string

### 4g. Update markdown documentation
### 3e. Update markdown documentation

#### CHANGELOG.md
Add a new section at the top (below the header), following the existing Keep a Changelog format:
Expand Down Expand Up @@ -219,31 +153,31 @@ After generation and doc updates, ensure the test suite is compatible with the n
- **Integration tests** (`tests/test_live.py`): Run against the real You.com API. Require `YOU_API_KEY_AUTH` env var.
- **Client tests** (`tests/test_client.py`): Test HTTP client setup helpers.

#### 4h-1. Update tests for new/changed APIs
#### 3f-1. Update tests for new/changed APIs

Review the generated diff from step 4c. If Speakeasy added, removed, or changed any models, endpoints, or parameters:
Review the generated diff from step 3b. If Speakeasy added, removed, or changed any models, endpoints, or parameters:

1. Update unit tests to reflect the new request/response shapes
2. Update integration tests (`test_live.py`) if endpoints or model imports changed
3. Add new test cases for any new endpoints or features

#### 4h-2. Run unit tests
#### 3f-2. Run unit tests

```bash
pytest tests/ --ignore=tests/test_live.py --ignore=tests/test_performance.py -v
```

If tests fail, fix the test code (or SDK issues if applicable) and re-run.

#### 4h-3. Run integration tests (if API key is available)
#### 3f-3. Run integration tests (if API key is available)

```bash
pytest tests/test_live.py -v
```

If `YOU_API_KEY_AUTH` is not set, skip this step and note it in the PR description.

#### 4h-4. Validate tests line by line
#### 3f-4. Validate tests line by line

After all tests pass, read through every changed test file line by line. Check for:
- Incorrect model imports that no longer exist
Expand All @@ -252,20 +186,16 @@ After all tests pass, read through every changed test file line by line. Check f
- Dead test cases for removed endpoints
- Inconsistencies between test expectations and the actual generated SDK code

If this review surfaces any changes needed, make the fixes and go back to step 4h-2. Repeat this loop until a full line-by-line review finds no additional changes needed.
If this review surfaces any changes needed, make the fixes and go back to step 3f-2. Repeat until a full line-by-line review finds no additional changes needed.

### 4i. Commit all changes

Stage and commit all generated and manually updated files to the release branch:
### 3g. Commit all changes

```bash
git add -A
git commit -m "feat: Python SDK X.Y.Z"
```

Do NOT commit `.speakeasy/workflow.yaml` if it still contains local spec overrides — it should have been reverted in step 4d.

### 4j. Push and open a PR
### 3h. Push and open a PR

```bash
git push -u origin release/X.Y.Z
Expand Down
137 changes: 15 additions & 122 deletions .github/workflows/generate-sdk.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,161 +7,54 @@ on:
description: 'Version to release (e.g. 2.4.0)'
required: true
type: string
issues:
types: [labeled]

permissions:
contents: write
pull-requests: write
issues: write
id-token: write

jobs:
generate-sdk:
if: |
github.event_name == 'workflow_dispatch' ||
(github.event_name == 'issues' && github.event.label.name == 'sdk-release')
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Parse inputs
id: parse
env:
EVENT_NAME: ${{ github.event_name }}
DISPATCH_VERSION: ${{ inputs.version }}
ISSUE_BODY: ${{ github.event.issue.body }}
run: |
if [ "$EVENT_NAME" = "workflow_dispatch" ]; then
echo "version=$DISPATCH_VERSION" >> "$GITHUB_OUTPUT"
echo "use_custom_specs=false" >> "$GITHUB_OUTPUT"
echo "custom_unified_agents=" >> "$GITHUB_OUTPUT"
echo "custom_search_v1=" >> "$GITHUB_OUTPUT"
echo "custom_contents=" >> "$GITHUB_OUTPUT"
echo "custom_base=" >> "$GITHUB_OUTPUT"
else
# Parse version from issue body (### Version section)
version=$(echo "$ISSUE_BODY" | sed -n '/### Version/{n;/^$/d;p;}' | head -1 | xargs)
echo "version=$version" >> "$GITHUB_OUTPUT"

# Parse which specs to override from the dropdown
overrides=$(echo "$ISSUE_BODY" | sed -n '/### Which specs to override/,/### /{/### Which specs to override/d;/### /d;p;}' | grep -v '^$' | head -1)

# Parse each custom spec textarea
has_custom=false

unified=$(echo "$ISSUE_BODY" | sed -n '/### Custom spec: unified agents/,/### /{/### Custom spec: unified agents/d;/### /d;p;}' | sed '/^$/d')
if [ -n "$unified" ] && [ "$unified" != "_No response_" ]; then
has_custom=true
# Write to file for multiline support
echo "$unified" > /tmp/custom_unified_agents.yaml
echo "custom_unified_agents=/tmp/custom_unified_agents.yaml" >> "$GITHUB_OUTPUT"
else
echo "custom_unified_agents=" >> "$GITHUB_OUTPUT"
fi

search=$(echo "$ISSUE_BODY" | sed -n '/### Custom spec: search v1/,/### /{/### Custom spec: search v1/d;/### /d;p;}' | sed '/^$/d')
if [ -n "$search" ] && [ "$search" != "_No response_" ]; then
has_custom=true
echo "$search" > /tmp/custom_search_v1.yaml
echo "custom_search_v1=/tmp/custom_search_v1.yaml" >> "$GITHUB_OUTPUT"
else
echo "custom_search_v1=" >> "$GITHUB_OUTPUT"
fi

contents=$(echo "$ISSUE_BODY" | sed -n '/### Custom spec: contents/,/### /{/### Custom spec: contents/d;/### /d;p;}' | sed '/^$/d')
if [ -n "$contents" ] && [ "$contents" != "_No response_" ]; then
has_custom=true
echo "$contents" > /tmp/custom_contents.yaml
echo "custom_contents=/tmp/custom_contents.yaml" >> "$GITHUB_OUTPUT"
else
echo "custom_contents=" >> "$GITHUB_OUTPUT"
fi

base=$(echo "$ISSUE_BODY" | sed -n '/### Custom spec: base/,/### \|$/{/### Custom spec: base/d;/### /d;p;}' | sed '/^$/d')
if [ -n "$base" ] && [ "$base" != "_No response_" ]; then
has_custom=true
echo "$base" > /tmp/custom_base.yaml
echo "custom_base=/tmp/custom_base.yaml" >> "$GITHUB_OUTPUT"
else
echo "custom_base=" >> "$GITHUB_OUTPUT"
fi

echo "use_custom_specs=$has_custom" >> "$GITHUB_OUTPUT"
fi

- uses: anthropics/claude-code-action@v1
id: claude
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
allowed_tools: 'Bash,Read,Write,Edit,Glob,Grep'
prompt: |
You are running a non-interactive SDK release workflow. All inputs have been provided — do NOT use AskUserQuestion at any point.
You are running a non-interactive SDK release workflow. Do NOT use AskUserQuestion at any point.

## Inputs

- **Version**: ${{ steps.parse.outputs.version }}
- **Use custom specs**: ${{ steps.parse.outputs.use_custom_specs }}
- **Custom spec file (unified agents)**: ${{ steps.parse.outputs.custom_unified_agents }}
- **Custom spec file (search v1)**: ${{ steps.parse.outputs.custom_search_v1 }}
- **Custom spec file (contents)**: ${{ steps.parse.outputs.custom_contents }}
- **Custom spec file (base)**: ${{ steps.parse.outputs.custom_base }}
- **Version**: ${{ inputs.version }}

## Instructions

Follow the generate-sdk-and-open-pr skill in `.agents/skills/generate-sdk-and-open-pr/SKILL.md`, but skip all `AskUserQuestion` steps — the inputs above replace those interactions. Specifically:

### Step 1: Handle custom specs

If "Use custom specs" is `true`, check which custom spec file paths are non-empty. For each non-empty path, read the file and update `.speakeasy/workflow.yaml` to point to that local file instead of the remote URL. Map:
- `custom_unified_agents` → replaces `https://you.com/specs/openapi_unified_agents.yaml`
- `custom_search_v1` → replaces `https://you.com/specs/openapi_search_v1.yaml`
- `custom_contents` → replaces `https://you.com/specs/openapi_contents.yaml`
- `custom_base` → replaces `https://you.com/specs/openapi_base.yaml`

Leave specs with empty paths unchanged (they keep the remote URL).
Follow the generate-sdk-and-open-pr skill in `.agents/skills/generate-sdk-and-open-pr/SKILL.md`, skipping all interactive steps. Specifically:

### Step 2: Check current versions
### Step 1: Check current versions

Run the version checks from the skill (git tags, gh release, PyPI, local files). Log the findings but do not ask for confirmation — proceed automatically.
Run the version checks (git tags, gh release, PyPI, local files). Log the findings but do not ask for confirmation — proceed automatically.

### Step 3: Use the provided version
### Step 2: Use the provided version

The version is `${{ steps.parse.outputs.version }}`. Do not suggest alternatives — use this version directly.
The version is `${{ inputs.version }}`. Use this version directly.

### Step 4: Generate and release
### Step 3: Generate and release

Follow steps 4b through 4j from the skill exactly:
- `speakeasy bump -v ${{ steps.parse.outputs.version }} -t you`
Follow steps 3a through 3i from the skill exactly:
- `speakeasy bump -v ${{ inputs.version }} -t you`
- `speakeasy run`
- Revert `.speakeasy/workflow.yaml` if custom specs were used
- Create branch `release/${{ steps.parse.outputs.version }}`
- Update versions in pyproject.toml and _version.py
- Create branch `release/${{ inputs.version }}`
- Update versions in pyproject.toml and _version.py if not already updated by Speakeasy
- Update CHANGELOG.md, MIGRATION.md (if major), verify USAGE.md and README.md
- Update and run tests: `pytest tests/ --ignore=tests/test_live.py --ignore=tests/test_performance.py -v`
- Fix any test failures and re-run until clean
- Commit: `git add -A && git commit -m "feat: Python SDK ${{ steps.parse.outputs.version }}"`
- Push: `git push -u origin release/${{ steps.parse.outputs.version }}`
- Open PR: `gh pr create --title "Python SDK ${{ steps.parse.outputs.version }}" --base main`
- Commit: `git add -A && git commit -m "feat: Python SDK ${{ inputs.version }}"`
- Push: `git push -u origin release/${{ inputs.version }}`
- Open PR: `gh pr create --title "Python SDK ${{ inputs.version }}" --base main`

Include a structured PR body with Summary, Changes (from changelog), and Checklist.

- name: Comment on issue and close
if: github.event_name == 'issues'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ISSUE_NUMBER: ${{ github.event.issue.number }}
VERSION: ${{ steps.parse.outputs.version }}
run: |
# Find the PR that was just created
pr_url=$(gh pr list --head "release/$VERSION" --json url --jq '.[0].url' 2>/dev/null || echo "")

if [ -n "$pr_url" ]; then
gh issue comment "$ISSUE_NUMBER" --body "SDK release workflow completed. PR opened: $pr_url"
else
gh issue comment "$ISSUE_NUMBER" --body "SDK release workflow completed for version $VERSION. Check the Actions tab for details."
fi

gh issue close "$ISSUE_NUMBER"
Loading