From 2813e49ca6196a67c5a33b0d37523276b1bfa49f Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 24 Feb 2026 10:24:54 +0000 Subject: [PATCH 1/5] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 7b2bedb3..8fa7f15a 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 645 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gcore%2Fgcore-2c42b1d05b4025a9622032db431d4596d1f461da59b300496420ff6ec45b92b3.yml -openapi_spec_hash: 7e82855b39236dacdf97fb96dc7149fc +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gcore%2Fgcore-520b868ef84890063a573a0950c561d928555de782b94a4964b81ddfac2b9965.yml +openapi_spec_hash: 312c4e3f3849455e8b59d4acd991d0f9 config_hash: 332323cce99008ceec46e04aeb672e0a From 90db06dccfcfd8dcc044818d89f851d97df4b7be Mon Sep 17 00:00:00 2001 From: Danil Krox Date: Tue, 24 Feb 2026 12:11:58 +0100 Subject: [PATCH 2/5] feat: add /release skill --- .agents/skills/release/SKILL.md | 220 ++++++++++++++++++ .agents/skills/release/references/products.md | 176 ++++++++++++++ .../references/release-notes-examples.md | 119 ++++++++++ .claude/skills | 1 + .opencode/commands/release.md | 5 + 5 files changed, 521 insertions(+) create mode 100644 .agents/skills/release/SKILL.md create mode 100644 .agents/skills/release/references/products.md create mode 100644 .agents/skills/release/references/release-notes-examples.md create mode 120000 .claude/skills create mode 100644 .opencode/commands/release.md diff --git a/.agents/skills/release/SKILL.md b/.agents/skills/release/SKILL.md new file mode 100644 index 00000000..71f5b371 --- /dev/null +++ b/.agents/skills/release/SKILL.md @@ -0,0 +1,220 @@ +--- +name: release +description: > + Release workflow for the G-Core/gcore-python SDK repository. Invoke with /release + to discover the open Stainless release PR, analyze its diff and changelog, + check CI status, merge with user confirmation, and generate human-readable + release notes on the GitHub Release. +disable-model-invocation: true +allowed-tools: > + Read, + Bash(gh pr list --repo G-Core/gcore-python *), + Bash(gh pr view * --repo G-Core/gcore-python *), + Bash(gh pr diff * --repo G-Core/gcore-python *), + Bash(gh pr checks * --repo G-Core/gcore-python *), + Bash(gh pr merge * --repo G-Core/gcore-python *), + Bash(gh release view --repo G-Core/gcore-python *), + Bash(gh release edit * --repo G-Core/gcore-python *), + Bash(sleep *) +--- + +# Release Skill — G-Core/gcore-python + +## Constraints + +- **Repository**: `G-Core/gcore-python` (hardcoded, do not use for other repos) +- **Allowed tools**: `gh` CLI (scoped to `G-Core/gcore-python`) and `Read` +- **Never**: modify source code, force-push, delete branches, or merge without + explicit user confirmation +- **Release PRs** are created by `stainless-app[bot]` with title `release: {version}` + +## Workflow + +Execute steps 1-6 in order. Present findings at each step before proceeding. + +### Step 1 — Discover the Release PR + +```bash +gh pr list --repo G-Core/gcore-python --state open --app stainless-app \ + --json number,title,author,url,createdAt +``` + +Find the PR whose title starts with `release: `. + +- If **no open release PR** exists, inform the user and stop. +- If found, display: PR number, title (contains version), URL, creation date. + +### Step 2 — Analyze the Release + +Fetch all three in parallel: + +1. PR body containing the auto-generated changelog (Part 2). Parse it to extract: + - Version number and date + - Breaking changes, Features, Bug Fixes, Refactors, Chores, Documentation + + ```bash + gh pr view {N} --repo G-Core/gcore-python --json body,title,number,url + ``` + +2. The actual code diff. Analyze for Python API-level changes: + - New/removed/renamed types, fields, methods + - Changed field types (e.g., `str` -> `Optional[str]`, `int` -> `Literal[""]`) + - New service methods (e.g., `create_and_poll()`) + - Changed return types + - Note: deprecation warnings (`warnings.warn`, `@deprecated`) are informational + only — the methods still exist in the SDK. Do **not** surface these as + user-facing changes in release notes unless the deprecation is new in this + release AND accompanied by a replacement in the same release. + + ```bash + gh pr diff {N} --repo G-Core/gcore-python + ``` + +3. List of changed files. Use commit scopes and file paths to infer product areas: + + ```bash + gh pr diff {N} --repo G-Core/gcore-python --name-only + ``` + + | File path prefix | Product Area | + |---|---| + | `resources/cdn/`, `types/cdn/` | **CDN** | + | `resources/cloud/`, `types/cloud/` | **Cloud** | + | `resources/security/`, `types/security/` | **DDoS Protection** | + | `resources/dns/`, `types/dns/` | **DNS** | + | `resources/fastedge/`, `types/fastedge/` | **FastEdge** | + | `resources/iam/`, `types/iam/` | **IAM** | + | `resources/storage/`, `types/storage/` | **Object Storage** | + | `resources/streaming/`, `types/streaming/` | **Streaming** | + | `resources/waap/`, `types/waap/` | **WAAP** | + | `_client.py`, `_base_client.py`, `_utils/`, `_models.py`, `_streaming.py`, `pagination.py`, `lib/` | **Other** | + + For canonical sub-product names within each product area, consult + `references/products.md`. Always use the exact names listed there. + + These are heuristics. When a scope or path does not clearly map, use + judgment based on the commit scope, file path, and diff context. + + Within a product area, split into distinct **sub-areas** by resource type. + Do not lump unrelated resources into a single sub-area. + +### Step 3 — Check CI Status + +```bash +gh pr checks {N} --repo G-Core/gcore-python --json name,state,bucket +``` + +Exit codes: `0` = all pass, `8` = pending, `1` = failure. + +| Status | Action | +|---|---| +| exit `0` / all checks pass | Report **CI green**, proceed | +| exit `8` / pending | Warn user checks are running. Ask: wait or proceed? | +| exit `1` / failure | Show failing checks. **Do not offer to merge.** | + +### Step 4 — Generate Human-Readable Release Notes + +Read `references/release-notes-examples.md` for style reference and examples. +Read `references/products.md` for canonical product and sub-product names. + +Using the changelog (Step 2.1) and diff analysis (Step 2.2), generate the +human-readable summary (Part 1) following this structure: + +```markdown +We're excited to announce version {VERSION}! + +### **{Product Area}** + +* **{Sub-area}** + * Added `{field/method}` to `{Type}` — {description} + * ⚠ BREAKING CHANGE: `{Type.field}` changed from `{old}` to `{new}` + * Deprecated `{service.method()}` — use `{alternative}` instead + * Fixed {description} — {detail} +``` + +#### Part 1 Rules + +- **Group by product area** (alphabetically: CDN, Cloud, DDoS Protection, + DNS, FastEdge, IAM, Object Storage, Streaming, WAAP). Place **Other** last. + Only include areas that have changes. +- **Within each area, group by sub-area** alphabetically (e.g., Bare Metal, + GPU Bare Metal, Managed Kubernetes, Load Balancers). Use the canonical names + from `references/products.md`. If changes do not fit a sub-area, list + directly under the product area. +- **Breaking changes** get `⚠ BREAKING CHANGE:` prefix inline. Always specify + old value/type and new value/type. +- **Deprecations**: ``Deprecated `method()` — use `alternative()` instead`` +- **Additions**: ``Added `name` field/method to `Type` — description`` +- **Fixes**: `Fixed {what} — {detail}` +- **Always use backtick-wrapped Python identifiers** for types, fields, methods. + Types use `PascalCase`, methods/fields use `snake_case`. +- **No commit hashes or links** in Part 1 (those are in Part 2). +- **Do not copy** the auto-generated changelog verbatim. Aggregate related + changes. Skip noise. +- **Omit `codegen metadata` and `aggregated API specs update`** entries unless + they introduce a specific user-visible change visible in the diff. + +Display the generated Part 1 to the user. Ask if they want to edit or approve. + +### Step 5 — Merge the Release PR + +Present to the user: +1. CI status (from Step 3) +2. Generated release notes preview (Part 1 from Step 4) +3. Auto-generated changelog (Part 2 from PR body) +4. Recommended merge method: **rebase** + +**Ask for explicit confirmation before merging.** + +If user declines or wants changes, return to Step 4. + +Once confirmed, merge via Bash: +```bash +gh pr merge {PR_NUMBER} --repo G-Core/gcore-python --rebase +``` + +If merge fails, report the error and stop. + +### Step 6 — Update the GitHub Release + +After merge, `stainless-app[bot]` auto-creates a GitHub Release. + +1. Fetch the latest release. Verify `tagName` matches expected version + `v{VERSION}`. If not found, `sleep 10` and retry once. + + ```bash + gh release view --repo G-Core/gcore-python --json tagName,body,url + ``` + +2. Build the final release body by combining Part 1 and the existing Part 2: + + ``` + {Part 1 — human-readable summary} + + + {Part 2 — auto-generated changelog already in release body} + ``` + +3. Update the release via Bash: + ```bash + gh release edit v{VERSION} \ + --repo G-Core/gcore-python \ + --notes "$(cat <<'RELEASE_EOF' + {combined release notes} + RELEASE_EOF + )" + ``` + +4. Display the release URL and confirm completion. + +## Failure Modes + +| Situation | Action | +|---|---| +| No open release PR | Inform user, stop | +| CI failing | Show failures, do not merge | +| CI pending | Warn, ask user preference | +| Merge conflict | Report, suggest manual resolution | +| Merge fails | Report error, stop | +| Release not found after merge | Retry once after 10s, then report | +| `gh` CLI not authenticated | Report, suggest `gh auth login` | diff --git a/.agents/skills/release/references/products.md b/.agents/skills/release/references/products.md new file mode 100644 index 00000000..48b913a8 --- /dev/null +++ b/.agents/skills/release/references/products.md @@ -0,0 +1,176 @@ +# Gcore Products — Canonical Names & File Path Heuristics + +Authoritative list of product and sub-product names for release notes. +Order products alphabetically; place "Other" last. Order sub-products +alphabetically within each product. + +## CDN + +SDK paths: `resources/cdn/`, `types/cdn/` + +| Sub-Product | File patterns | +|---|---| +| Activity Logs | `cdn/audit_logs*` | +| CDN Resources | `cdn/cdn_resources/cdn_resources*` (not rules, not shield) | +| CDN Service | `cdn/cdn.py` (service-level config) | +| IP Ranges | `cdn/ip_ranges*` | +| Logs | `cdn/logs.py`, `cdn/logs/` | +| Logs Uploader | `cdn/logs_uploader/` | +| Metrics | `cdn/metrics*` | +| Network Capacity | `cdn/network_capacity*` | +| Origin Groups | `cdn/origin_groups*` | +| Origin Shielding | `cdn/cdn_resources/shield*` | +| Regions | `cdn/regions/` | +| Rule Templates | `cdn/rule_templates*` | +| Rules | `cdn/cdn_resources/rules*` | +| SSL Certificates | `cdn/certificates*` | +| Statistics | `cdn/statistics*` | +| Trusted CA Certificates | `cdn/trusted_ca_certificates*` | + +## Cloud + +SDK paths: `resources/cloud/`, `types/cloud/` + +| Sub-Product | File patterns | +|---|---| +| Bare Metal | `cloud/baremetal/` | +| Billing Reservations | `cloud/billing_reservations*` | +| Container as a Service | `cloud/containers/` | +| Cost Reports | `cloud/cost_reports*`, `cloud/usage_reports*` | +| Databases | `cloud/databases/` | +| Everywhere Inference | `cloud/inference/` (not `inference/applications/`) | +| Everywhere Inference Apps | `cloud/inference/applications/` | +| File Shares | `cloud/file_shares/` | +| Floating IPs | `cloud/floating_ips*` | +| GPU Bare Metal | `cloud/gpu_baremetal/`, `cloud/gpu_baremetal_clusters/` | +| GPU Virtual | `cloud/gpu_virtual/`, `cloud/gpu_virtual_clusters/` | +| Instances | `cloud/instances/` | +| IP Ranges | `cloud/ip_ranges*` | +| Load Balancers | `cloud/load_balancers/` | +| Managed Kubernetes | `cloud/k8s/` | +| Networks | `cloud/networks/` | +| Placement Groups | `cloud/placement_groups*` | +| Projects | `cloud/projects*` | +| Quotas | `cloud/quotas/` | +| Regions | `cloud/regions*` | +| Registries | `cloud/registries/` | +| Reserved IPs | `cloud/reserved_fixed_ips/` | +| Secrets | `cloud/secrets*` | +| Security Groups | `cloud/security_groups/` | +| SSH Keys | `cloud/ssh_keys*` | +| Tasks | `cloud/tasks*` | +| User Actions | `cloud/audit_logs*` | +| Users | `cloud/users/` | +| Volume Snapshots | `cloud/volume_snapshots*` | +| Volumes | `cloud/volumes*` | + +## DDoS Protection + +SDK paths: `resources/security/`, `types/security/` + +| Sub-Product | File patterns | +|---|---| +| BGP Announces | `security/bgp_announces*` | +| Event Logs | `security/events*` | +| Profiles | `security/profiles*` (not `profile_templates*`) | +| Security Templates | `security/profile_templates*` | + +## DNS + +SDK paths: `resources/dns/`, `types/dns/` + +| Sub-Product | File patterns | +|---|---| +| DNSSEC | `dns/zones/dnssec*` | +| Locations | `dns/locations*` | +| Metrics | `dns/metrics*` | +| Network Mappings | `dns/network_mappings*` | +| Pickers | `dns/pickers/` | +| RRsets | `dns/zones/rrsets*` | +| Zones | `dns/zones/zones*` | + +## FastEdge + +SDK paths: `resources/fastedge/`, `types/fastedge/` + +| Sub-Product | File patterns | +|---|---| +| Apps | `fastedge/apps/` | +| Binaries | `fastedge/binaries*` | +| Edge Storage | `fastedge/kv_stores*` | +| Secrets | `fastedge/secrets*` | +| Statistics | `fastedge/statistics*` | +| Templates | `fastedge/templates*` | + +## IAM + +SDK paths: `resources/iam/`, `types/iam/` + +| Sub-Product | File patterns | +|---|---| +| Account | `iam/iam.py` (service-level auth) | +| API Tokens | `iam/api_tokens*` | +| Users | `iam/users*` | + +## Object Storage + +SDK paths: `resources/storage/`, `types/storage/` + +| Sub-Product | File patterns | +|---|---| +| Buckets | `storage/buckets/buckets*` (not `cors*`, `lifecycle*`, `policy*`) | +| CORS | `storage/buckets/cors*` | +| Credentials | `storage/credentials*` | +| Lifecycle | `storage/buckets/lifecycle*` | +| Locations | `storage/locations*` | +| Policies | `storage/buckets/policy*` | +| Statistics | `storage/statistics*` | + +## Streaming + +SDK paths: `resources/streaming/`, `types/streaming/` + +| Sub-Product | File patterns | +|---|---| +| AI | `streaming/ai_tasks*` | +| Broadcasts | `streaming/broadcasts*` | +| Directories | `streaming/directories*` | +| Overlays | `streaming/streams/overlays*` | +| Players | `streaming/players*` | +| Playlists | `streaming/playlists/` | +| Quality Sets | `streaming/quality_sets*` | +| Restreams | `streaming/restreams*` | +| Statistics | `streaming/statistics*` | +| Streams | `streaming/streams/streams*` | +| Subtitles | `streaming/videos/subtitles*` | +| Videos | `streaming/videos/videos*` | + +## WAAP + +SDK paths: `resources/waap/`, `types/waap/` + +| Sub-Product | File patterns | +|---|---| +| Advanced Rules | `waap/advanced_rules*`, `waap/domains/advanced_rules*` | +| Analytics | `waap/domains/statistics*`, `waap/statistics*`, `waap/domains/analytics/` | +| API Discovery | `waap/domains/api_discovery*`, `waap/domains/api_paths*`, `waap/domains/api_path_groups*` | +| Custom Page Sets | `waap/custom_page_sets*` | +| Custom Rules | `waap/domains/custom_rules*` | +| Domains | `waap/domains/domains*`, `waap/domains/settings*` | +| Firewall Rules | `waap/domains/firewall_rules*` | +| IP Spotlight | `waap/ip_info/` | +| Network Organizations | `waap/organizations*` | +| Security Insights | `waap/domains/insights*`, `waap/insights*`, `waap/domains/insight_silences*` | +| Tags | `waap/tags*` | +| WAAP Service | `waap/waap.py` | + +## Other + +Changes not specific to any product (SDK internals, utilities, shared code). + +| File patterns | +|---| +| `.github/*`, `.stats.yml`, `pyproject.toml`, `requirements*.txt`, `README.md` | +| `_client.py`, `_base_client.py`, `_models.py`, `_streaming.py`, `pagination.py` | +| `_utils/`, `_compat.py`, `_constants.py`, `_exceptions.py`, `_files.py` | +| `lib/`, `_response.py`, `_types.py` | diff --git a/.agents/skills/release/references/release-notes-examples.md b/.agents/skills/release/references/release-notes-examples.md new file mode 100644 index 00000000..efd680d6 --- /dev/null +++ b/.agents/skills/release/references/release-notes-examples.md @@ -0,0 +1,119 @@ +# Release Notes Examples + +Real examples from this repository showing the target format. Use these as +style references when generating human-readable release notes (Part 1). + +## Example 1: v0.32.0 (multiple product areas, breaking changes inline) + +```markdown +We're excited to announce version 0.32.0! + +### **CDN** + +* **CDN Resources** + * ⚠ BREAKING CHANGE: `origin` in `CDNResourceCreateParams` changed from required `str` to optional — exactly one of `origin` or `origin_group` must be provided + * ⚠ BREAKING CHANGE: `origin_group` in `CDNResourceCreateParams` changed from required `int` to optional + +* **Logs Uploader** + * Added `endpoint` field to S3 OSS config — custom S3 endpoint now supported in `LogsUploaderTarget` and related params + +### **Cloud** + +* **Load Balancers** + * Added `admin_state_up` field across all load balancer, listener, pool, and health monitor response and param types + * ⚠ BREAKING CHANGE: `secret_id` in listener create params changed type for correct enum handling + * Fixed `secret_id` type mismatch in `listeners.create_and_poll()` — aligned to `Literal[""]` to match `create()` signature + +* **Managed Kubernetes** + * Added `include_capacity` param to `K8sFlavorListParams` for flavor capacity info + * Added `CLUSTER_REBUILD` and `CLUSTER_SERVER_REBUILD` task states + * ⚠ BREAKING CHANGE: `K8sClusterKubeconfig.created_at` and `expires_at` changed from nullable to required + +### **FastEdge** + +* ⚠ BREAKING CHANGE: `App.stores` type changed from `Dict[str, int]` to `Dict[str, AppStore]` — stores now carry `id`, `name`, and `comment` +* ⚠ BREAKING CHANGE: `KvStore` response restructured — `id` field removed, `name` is now primary identifier, `updated` renamed to `updated_at`, added `revision` and `size` fields +* ⚠ BREAKING CHANGE: `kv_stores.new()` return type changed from `KvStore` to `KvStoreCreateResponse` +* ⚠ BREAKING CHANGE: `kv_stores.get()` return type changed from `KvStoreGetResponse` to `KvStore` +* ⚠ BREAKING CHANGE: Removed `KvStoreStats` and `KvStoreGetResponse` types +* ⚠ BREAKING CHANGE: Removed `app_limit`, `daily_limit`, `hourly_limit` fields from `Client` type +* Renamed "KV Storage" to "Edge Storage" across all endpoints +* Added `store` as new `TemplateParameterDataType` enum value + +### **WAAP** + +* **Analytics** + * Added `decision` and `optional_action` fields to `WaapRequestDetails` and `WaapRequestSummary` + * Added `domain_id` field to `WaapRequestSummary` + * Deprecated `domain_statistics.get_traffic_series()` — use `GET /v1/analytics/traffic` instead + +### **Other** + +* Fixed lint compatibility with Python 3.14 +``` + +## Example 2: v0.31.0 (breaking renames, SDK improvements) + +```markdown +We're excited to announce version 0.31.0! + +### **CDN** + +* **API Naming** + * ⚠ BREAKING CHANGE: Renamed all `Cdn*` types to `CDN*` for consistent Python naming conventions - `CdnResource` is now `CDNResource`, `CdnAccount` is now `CDNAccount`, `CdnAccountLimits` is now `CDNAccountLimits`, etc. + * ⚠ BREAKING CHANGE: Renamed `resources` service to `cdn_resources` - access CDN resources via `client.cdn.cdn_resources` instead of `client.cdn.resources` + +* **User-Agent ACL** + * Added regex pattern support - you can now use regular expressions in User-Agent ACL rules with `~` (case-sensitive) or `~*` (case-insensitive) prefix + +### **WAAP** + +* **Analytics** + * Deprecated `get_requests_series()` method - use the new `/v1/analytics/requests` endpoint instead + * Updated action filter values - changed from "block", "captcha", "handshake", "monitor" to "allow", "block", "captcha", "handshake" + +### **Other** + +* Added custom JSON encoder for extended type support - automatically handles `datetime` objects and `pydantic.BaseModel` instances when serializing request bodies +``` + +## Example 3: v0.30.0 (Cloud-focused, v2 API migrations) + +```markdown +We're excited to announce version 0.30.0! + +### **Cloud** + +* **Floating IPs** + * ⚠ BREAKING CHANGE: Migrated `update()` to v2 API endpoint - now uses `/v2/floatingips/` and returns `TaskIDList` instead of `FloatingIP` + * ⚠ BREAKING CHANGE: Added `port_id` and `fixed_ip_address` parameters to `update()` - use these for assignment operations instead of the deprecated `assign()` method + * Added `update_and_poll()` method - updates a floating IP and waits for the operation to complete, returns `FloatingIP` + +* **Security Groups** + * ⚠ BREAKING CHANGE: Migrated to v2 API endpoints - `create()` and `update()` methods now use `/v2/security_groups/` endpoints and return `TaskIDList` instead of `SecurityGroup` + * ⚠ BREAKING CHANGE: Simplified `create()` parameters - `name`, `description`, `rules`, and `tags` are now top-level parameters; removed nested `security_group` wrapper + * ⚠ BREAKING CHANGE: Removed `instances` parameter from `create()` - use dedicated instance methods to assign security groups to instances + * ⚠ BREAKING CHANGE: Changed `update()` to use declarative rules - `changed_rules` replaced with `rules` parameter; specify the complete desired state instead of create/delete actions + * ⚠ BREAKING CHANGE: Added `description` parameter to `update()` - can now update security group description + * Added `create_and_poll()` method - creates a security group and waits for the operation to complete, returns `SecurityGroup` + * Added `update_and_poll()` method - updates a security group and waits for the operation to complete, returns `SecurityGroup` + +* **Tasks** + * Added `update_floating_ip` and `update_router` task types to `task_type` filter - filter tasks by these new operation types in `list()` method +``` + +## Style Rules (inferred from examples) + +1. **Opening line**: Always `We're excited to announce version {VERSION}!` +2. **Product area headers**: `### **{Area}**` — bold inside h3 +3. **Sub-area items**: `* **{Sub-area}**` — bold bullet, followed by indented child bullets +4. **When a product area has no sub-areas** (e.g., FastEdge above): list items directly under the product header without a sub-area bullet +5. **Breaking changes**: Inline with `⚠ BREAKING CHANGE:` prefix, include old type/value -> new type/value +6. **Deprecations**: Use format ``Deprecated `method()` — use `alternative()` instead`` +7. **New fields/methods**: Use format ``Added `field_name` field to `TypeName` — description`` +8. **Fixes**: Use format `Fixed {what} — {detail}` +9. **Type references**: Always use backtick-wrapped Python identifiers (e.g., `Optional[str]`, `Dict[str, int]`, `CDNResource`) +10. **Method references**: Use `snake_case` for methods (e.g., `create_and_poll()`, `get_traffic_series()`) +11. **Descriptions**: Short, specific, user-actionable. No commit hashes in Part 1. +12. **Product area order**: Alphabetical. Place "Other" last. +13. **Sub-area order**: Alphabetical within each product area. diff --git a/.claude/skills b/.claude/skills new file mode 120000 index 00000000..2b7a412b --- /dev/null +++ b/.claude/skills @@ -0,0 +1 @@ +../.agents/skills \ No newline at end of file diff --git a/.opencode/commands/release.md b/.opencode/commands/release.md new file mode 100644 index 00000000..5e598f68 --- /dev/null +++ b/.opencode/commands/release.md @@ -0,0 +1,5 @@ +--- +description: Merge release PR and generate release notes for G-Core/gcore-python +--- + +Load and execute the "release" skill. Follow its workflow instructions exactly. From b0d1b0dc11b92176083d7d467e28d1e52c253c7f Mon Sep 17 00:00:00 2001 From: Pedro Oliveira <8281907+pedrodeoliveira@users.noreply.github.com> Date: Tue, 24 Feb 2026 11:33:41 +0000 Subject: [PATCH 3/5] fix(cloud): get cluster_id from task created_resources.clusters Use created_resources.clusters instead of created_resources.ai_clusters for GPU baremetal cluster create_and_poll, now that the API fills the correct field. --- .../resources/cloud/gpu_baremetal/clusters/clusters.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/gcore/resources/cloud/gpu_baremetal/clusters/clusters.py b/src/gcore/resources/cloud/gpu_baremetal/clusters/clusters.py index adfb58cb..700297ba 100644 --- a/src/gcore/resources/cloud/gpu_baremetal/clusters/clusters.py +++ b/src/gcore/resources/cloud/gpu_baremetal/clusters/clusters.py @@ -668,9 +668,9 @@ def create_and_poll( polling_interval_seconds=polling_interval_seconds, polling_timeout_seconds=polling_timeout_seconds, ) - if not task.created_resources or not task.created_resources.ai_clusters: + if not task.created_resources or not task.created_resources.clusters: raise ValueError("No cluster was created") - cluster_id = task.created_resources.ai_clusters[0] + cluster_id = task.created_resources.clusters[0] return self.get( # pyright: ignore[reportDeprecated] cluster_id=cluster_id, project_id=project_id, @@ -1387,9 +1387,9 @@ async def create_and_poll( polling_interval_seconds=polling_interval_seconds, polling_timeout_seconds=polling_timeout_seconds, ) - if not task.created_resources or not task.created_resources.ai_clusters: + if not task.created_resources or not task.created_resources.clusters: raise ValueError("No cluster was created") - cluster_id = task.created_resources.ai_clusters[0] + cluster_id = task.created_resources.clusters[0] return await self.get( # pyright: ignore[reportDeprecated] cluster_id=cluster_id, project_id=project_id, From 46ef3348dcfd5ca7c1ecd8dd5f8ea9f8d46d230d Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 24 Feb 2026 12:24:47 +0000 Subject: [PATCH 4/5] feat(api): aggregated API specs update --- .stats.yml | 4 ++-- src/gcore/resources/cloud/regions.py | 16 ++++++++++++---- src/gcore/types/cloud/region_list_params.py | 3 +++ tests/api_resources/cloud/test_regions.py | 2 ++ 4 files changed, 19 insertions(+), 6 deletions(-) diff --git a/.stats.yml b/.stats.yml index 8fa7f15a..efb5f168 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 645 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gcore%2Fgcore-520b868ef84890063a573a0950c561d928555de782b94a4964b81ddfac2b9965.yml -openapi_spec_hash: 312c4e3f3849455e8b59d4acd991d0f9 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/gcore%2Fgcore-2f258036cad399055647446f0aae41c40f29bf6b486de68ed565653c10adb569.yml +openapi_spec_hash: 319b97727a2c05125aa27228db52053c config_hash: 332323cce99008ceec46e04aeb672e0a diff --git a/src/gcore/resources/cloud/regions.py b/src/gcore/resources/cloud/regions.py index b9d06835..c25f69f5 100644 --- a/src/gcore/resources/cloud/regions.py +++ b/src/gcore/resources/cloud/regions.py @@ -47,6 +47,7 @@ def with_streaming_response(self) -> RegionsResourceWithStreamingResponse: def list( self, *, + display_name: str | Omit = omit, limit: int | Omit = omit, offset: int | Omit = omit, order_by: Literal["created_at.asc", "created_at.desc", "display_name.asc", "display_name.desc"] | Omit = omit, @@ -62,9 +63,11 @@ def list( """List regions Args: - limit: Limit the number of returned regions. + display_name: Filter regions by display name. - Falls back to default of 100 if not + Case-insensitive exact match. + + limit: Limit the number of returned regions. Falls back to default of 100 if not specified. Limited by max limit value of 1000 offset: Offset value is used to exclude the first set of records from the result @@ -94,6 +97,7 @@ def list( timeout=timeout, query=maybe_transform( { + "display_name": display_name, "limit": limit, "offset": offset, "order_by": order_by, @@ -173,6 +177,7 @@ def with_streaming_response(self) -> AsyncRegionsResourceWithStreamingResponse: def list( self, *, + display_name: str | Omit = omit, limit: int | Omit = omit, offset: int | Omit = omit, order_by: Literal["created_at.asc", "created_at.desc", "display_name.asc", "display_name.desc"] | Omit = omit, @@ -188,9 +193,11 @@ def list( """List regions Args: - limit: Limit the number of returned regions. + display_name: Filter regions by display name. + + Case-insensitive exact match. - Falls back to default of 100 if not + limit: Limit the number of returned regions. Falls back to default of 100 if not specified. Limited by max limit value of 1000 offset: Offset value is used to exclude the first set of records from the result @@ -220,6 +227,7 @@ def list( timeout=timeout, query=maybe_transform( { + "display_name": display_name, "limit": limit, "offset": offset, "order_by": order_by, diff --git a/src/gcore/types/cloud/region_list_params.py b/src/gcore/types/cloud/region_list_params.py index ac7b106b..9713a729 100644 --- a/src/gcore/types/cloud/region_list_params.py +++ b/src/gcore/types/cloud/region_list_params.py @@ -8,6 +8,9 @@ class RegionListParams(TypedDict, total=False): + display_name: str + """Filter regions by display name. Case-insensitive exact match.""" + limit: int """Limit the number of returned regions. diff --git a/tests/api_resources/cloud/test_regions.py b/tests/api_resources/cloud/test_regions.py index 9f31384c..7369f789 100644 --- a/tests/api_resources/cloud/test_regions.py +++ b/tests/api_resources/cloud/test_regions.py @@ -26,6 +26,7 @@ def test_method_list(self, client: Gcore) -> None: @parametrize def test_method_list_with_all_params(self, client: Gcore) -> None: region = client.cloud.regions.list( + display_name="Luxembourg", limit=100, offset=0, order_by="created_at.desc", @@ -107,6 +108,7 @@ async def test_method_list(self, async_client: AsyncGcore) -> None: @parametrize async def test_method_list_with_all_params(self, async_client: AsyncGcore) -> None: region = await async_client.cloud.regions.list( + display_name="Luxembourg", limit=100, offset=0, order_by="created_at.desc", From 5808aa7f4d2e381cbed7798fad48035a9d3fa7ad Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 24 Feb 2026 12:25:08 +0000 Subject: [PATCH 5/5] release: 0.35.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 14 ++++++++++++++ pyproject.toml | 2 +- src/gcore/_version.py | 2 +- 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index e4e1c3ce..ce5e5c7c 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.34.0" + ".": "0.35.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 6afe9327..e2a6eb33 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # Changelog +## 0.35.0 (2026-02-24) + +Full Changelog: [v0.34.0...v0.35.0](https://github.com/G-Core/gcore-python/compare/v0.34.0...v0.35.0) + +### Features + +* add /release skill ([90db06d](https://github.com/G-Core/gcore-python/commit/90db06dccfcfd8dcc044818d89f851d97df4b7be)) +* **api:** aggregated API specs update ([46ef334](https://github.com/G-Core/gcore-python/commit/46ef3348dcfd5ca7c1ecd8dd5f8ea9f8d46d230d)) + + +### Bug Fixes + +* **cloud:** get cluster_id from task created_resources.clusters ([b0d1b0d](https://github.com/G-Core/gcore-python/commit/b0d1b0dc11b92176083d7d467e28d1e52c253c7f)) + ## 0.34.0 (2026-02-23) Full Changelog: [v0.33.0...v0.34.0](https://github.com/G-Core/gcore-python/compare/v0.33.0...v0.34.0) diff --git a/pyproject.toml b/pyproject.toml index 30f18d33..8d80f22d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "gcore" -version = "0.34.0" +version = "0.35.0" description = "The official Python library for the gcore API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/gcore/_version.py b/src/gcore/_version.py index 506bfca7..7bcbde4b 100644 --- a/src/gcore/_version.py +++ b/src/gcore/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "gcore" -__version__ = "0.34.0" # x-release-please-version +__version__ = "0.35.0" # x-release-please-version