Skip to content
Merged
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
4 changes: 3 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,10 +81,12 @@ cargo fmt
# Type-check only (fast)
cargo check

# Regenerate the embedded API catalog
# Regenerate the embedded API catalog (needs access to GoDaddy's internal spec repos)
cargo run -p generate-api-catalog
```

If the CI "API catalog drift" check fails, see [Updating embedded API specs](docs/maintainers.md#updating-embedded-api-specs).

Adding a pre-release command, or wondering why one is hidden behind the default build? See [`docs/feature-flags.md`](docs/feature-flags.md).

## Code Review
Expand Down
21 changes: 21 additions & 0 deletions docs/maintainers.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ If you are a new developer on this project, read the following to get started:
- [This repo's docs](../docs/)
- [Command authoring checklist](./command-authoring.md) covers conventions for adding commands and command groups.
- [`CONTRIBUTING.md`](../CONTRIBUTING.md) gives some general tips for setting up your workspace for development.
- [Updating embedded API specs](#updating-embedded-api-specs) explains how to resolve a failing API catalog drift check.

If you are doing agentic coding and see your agent struggling with following standards correctly, contributions to [`AGENTS.md`](../AGENTS.md) are greatly appreciated.

Expand Down Expand Up @@ -52,3 +53,23 @@ To update this file:

1. Edit [`reviewers.json`](../.github/scripts/reviewers.json) to add per-product paths, reviewer GitHub usernames, or new per-product config sections. Sort entries alphabetically so it's easy to navigate.
2. Run `bash .github/scripts/generate-codeowners.sh` to regenerate the file from that configuration (it uses Bash-only features, so plain `sh` fails on some systems).

## Updating embedded API specs

The CLI embeds a catalog of upstream API specs (browsable with `gddy api ...`) and generates some typed clients from them. The [API catalog drift](../.github/workflows/catalog-drift.yml) workflow regenerates everything weekly and on every push to `main`, and fails when the committed copy is stale. To fix a failure:

1. Get a token that can read the spec repositories in the `gdcorp-platform` org. `gh auth token` works if your account has access; CI uses a read-only GitHub App instead.
1. From `rust/`, run `GITHUB_TOKEN=<token> cargo run -p generate-api-catalog`. Which specs are pulled is configured in [`api-catalog-sources.json`](../rust/api-catalog-sources.json).
1. Review the diff. The generator rewrites these paths:
- [`rust/schemas/api/`](../rust/schemas/api) — the embedded catalog. `manifest.json` carries a generated-at timestamp that changes on every run; the workflow ignores it, so a timestamp-only change is not drift.
- `rust/domains-client/openapi/domains.oas3.json`, `rust/hosting-client/openapi/hosting.oas3.json`, `rust/email-client/openapi/email.oas3.json` and `rust/shopping-client/openapi/shopping.oas3.json` — inputs to the generated clients.
- `rust/platform-app-client/graphql/schema.graphql` — input to the platform app client.

The drift workflow compares only the catalog, the Domains spec and the Hosting spec, so it can pass while the other codegen specs are stale. Commit everything the generator changed, not only what the workflow flagged.
1. Run the checks in [`AGENTS.md`](../AGENTS.md#verification-checklist-required-before-finishing-work). Generated clients are rebuilt from the updated specs, so an upstream breaking change shows up as a compile error or test failure here; fix the affected commands in the same PR.
1. Commit the result as `fix(api-catalog): resync spec drift with ...` and name the APIs that changed (see [#321](https://github.com/godaddy/cli/pull/321)), since the commit title is surfaced in release notes.

Notes:

- The domains v1 `agreements` spec comes from a public endpoint and is best-effort: if the fetch fails, the generator logs a warning and leaves `domains.oas3.json` untouched. Set `SKIP_DOMAINS_REFRESH` to skip that fetch deliberately, for example when working offline. Don't commit with it set unless you mean to leave that file alone.
- Operations the upstream spec marks `x-visibility: private` are intentionally dropped from the catalog, so they will not appear after a refresh.
52 changes: 41 additions & 11 deletions rust/domains-client/openapi/domains.oas3.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,17 @@
}
},
"parameters": {
"allowNameserverChange": {
"description": "Consent to move the whole domain to GoDaddy's nameservers when it does not already use them; forwarding only works on GoDaddy nameservers. Without it, such a request fails with 409 NAMESERVER_CHANGE_REQUIRED; it has no other effect on a domain already on GoDaddy nameservers. The domain then gets a GoDaddy DNS zone with only default records, so records from the previous DNS provider, such as MX for email, are lost. The change propagates asynchronously (watch nameServers on GET /domain-names/{domain-name}) and is not undone when the forwarding is deleted. Callers who set it to true must also hold domains.nameserver:update, whatever the domain's nameservers; without it the request fails with 403.\n",
"example": true,
"in": "query",
"name": "allowNameserverChange",
"required": false,
"schema": {
"default": false,
"type": "boolean"
}
},
"domainNamePath": {
"description": "The domain name in punycode A-label form (e.g., example.com). For IDNs, use the punycode representation.\n",
"example": "example.com",
Expand All @@ -43,6 +54,16 @@
"type": "string"
}
},
"fqdnPath": {
"description": "The forwarded name in punycode A-label form: the domain itself (example.com), one of its subdomains (shop.example.com), or a wildcard directly on the domain (*.example.com), without a trailing dot. Matched case-insensitively.\n",
"example": "shop.example.com",
"in": "path",
"name": "fqdn",
"required": true,
"schema": {
"type": "string"
}
},
"idempotencyKey": {
"description": "Client-generated unique key (UUID recommended). Retrying a mutating request with the same Idempotency-Key returns the original response without creating a duplicate side effect. Required on all mutation endpoints to ensure safe retries.\n",
"example": "9f1c2e7a-4b3d-4e8f-a1c2-3d4e5f6a7b8c",
Expand Down Expand Up @@ -754,7 +775,9 @@
"type": "integer"
},
"matchingType": {
"$ref": "#/components/schemas/TLSAMatchingType"
"$ref": "#/components/schemas/TLSAMatchingType",
"description": "TLSA matching type (RFC 6698; TLSA only). Specifies how the selected certificate material is compared to the association data. Supplying this field for any other record type is ignored.\n",
"example": 1
},
"name": {
"description": "The DNS record name (label), relative to the zone apex. Use @ to represent the zone apex itself. Wildcards (*) are supported for A, AAAA, and CNAME records.\n",
Expand Down Expand Up @@ -790,7 +813,9 @@
"type": "string"
},
"selector": {
"$ref": "#/components/schemas/TLSASelector"
"$ref": "#/components/schemas/TLSASelector",
"description": "TLSA selector (RFC 6698; TLSA only). Specifies which part of the TLS certificate is matched against the association data. Supplying this field for any other record type is ignored.\n",
"example": 1
},
"service": {
"description": "Service name for SRV records, prefixed with an underscore (e.g. `_http`, `_smtp`). Combined with the protocol to form the record name as `_service._proto.name`.\n",
Expand Down Expand Up @@ -821,7 +846,9 @@
]
},
"usage": {
"$ref": "#/components/schemas/TLSAUsage"
"$ref": "#/components/schemas/TLSAUsage",
"description": "TLSA certificate usage (RFC 6698; TLSA only). Determines how certificate association data is interpreted during DANE authentication. Supplying this field for any other record type is ignored.\n",
"example": 3
},
"weight": {
"description": "Relative weight for load distribution among SRV records with equal priority. Higher values increase the probability of selection. Use 0 when only one target exists at a given priority.\n",
Expand Down Expand Up @@ -1970,7 +1997,8 @@
"links": {
"description": "An array of error-related HATEOAS links.",
"items": {
"$ref": "#/components/schemas/link-description"
"$ref": "#/components/schemas/link-description",
"readOnly": true
},
"nullable": true,
"readOnly": true,
Expand Down Expand Up @@ -2090,7 +2118,8 @@
"type": "string"
},
"countryCode": {
"$ref": "#/components/schemas/country-code"
"$ref": "#/components/schemas/country-code",
"description": "The [two-character ISO 3166-1 code](https://en.wikipedia.org/wiki/ISO_3166-1) that identifies the country or region. Note: The country code for Great Britain is `GB` and not `UK` as used in the top-level domain names for that country. Use country code `C2` for China for comparable uncontrolled price (CUP) method, bank-card, and cross-border transactions."
},
"line1": {
"description": "The first line of the address. For example, number or street.",
Expand Down Expand Up @@ -2143,16 +2172,17 @@
},
"securitySchemes": {
"oauth2": {
"description": "GoDaddy OAuth 2.0 access token. The scope(s) listed on each operation are enforced per-operation. A token requires at least one of the scopes listed for that operation.\n",
"description": "GoDaddy OAuth 2.0 access token. The scope(s) listed on each operation are enforced per-operation. A token requires at least one of the scopes listed for that operation. Some parameters require an additional scope; this is stated on the parameter and on the operation's 403 response.\n",
"flows": {
"authorizationCode": {
"authorizationUrl": "https://api.godaddy.com/v2/oauth2/authorize",
"scopes": {
"domains.dns:update": "Create, update, and delete DNS zone records.\n",
"domains.domain:create": "Register domains. Requires a prior quoteToken.\n",
"domains.domain:read": "Read domain records, availability, suggestions, quotes, and operations.\n",
"domains.domain:update": "Update domain-level properties such as auto-renew, transfer-lock settings, and renewing owned domains. Requires a prior quoteToken for renewal. This scope covers mutations to the domain resource itself; use resource-specific scopes for nameservers (domains.nameserver:update) and DNS records (domains.dns:update).\n",
"domains.nameserver:update": "Manage authoritative nameservers for a domain, including setting custom nameservers and resetting to registry defaults.\n",
"domains.domain:read": "Read domain records, availability, suggestions, quotes, operations, and forwarding.\n",
"domains.domain:update": "Update domain-level properties such as auto-renew, transfer-lock settings, and renewing owned domains. Requires a prior quoteToken for renewal. This scope covers mutations to the domain resource itself; use resource-specific scopes for nameservers (domains.nameserver:update), DNS records (domains.dns:update), and forwarding (domains.forwarding:update).\n",
"domains.forwarding:update": "Create, replace, and delete forwarding for a domain and its subdomains. Setting allowNameserverChange=true, which moves the domain to GoDaddy nameservers, also requires domains.nameserver:update.\n",
"domains.nameserver:update": "Manage authoritative nameservers for a domain, including setting custom nameservers and resetting to registry defaults. Also required to create or replace forwarding with allowNameserverChange=true, which moves the domain to GoDaddy nameservers.\n",
"domains.registration-profile:create": "Create saved registration profiles.\n",
"domains.registration-profile:delete": "Delete saved registration profiles.\n",
"domains.registration-profile:read": "Read saved registration profiles, including the contact records stored on them.\n"
Expand All @@ -2165,9 +2195,9 @@
}
},
"info": {
"description": "The GoDaddy Domain Lifecycle Management API provides comprehensive capabilities\nfor discovering, registering, managing, renewing, transferring, and reselling\ndomain names. This is major version 3, designed for agent-first interactions\nwhile remaining fully usable by direct API clients and resellers.\n\n## Namespace\n\nAll paths are under `/v3/domains/`. The namespace is `domains` (the business capability);\nthe core entity collection is `/domain-names`.\n\n## Key Conventions\n\n**Quote/execute for commercial operations.** Every commercial mutation (register,\nrenew, transfer) requires a `quoteToken` minted by the corresponding quote\ncollection endpoint. Execution without a prior quote is structurally impossible.\nThe token locks the price, resolved settings, and required legal agreements for a\n10-minute TTL. Quote calls are free, read-only, and safe to call speculatively.\n\n**Async commercial operations.** `POST /registrations`, `POST /renewals`, and\n`POST /transfers` each return `202 Accepted` with the concrete entity body\n(`Registration`, `Renewal`, or `Transfer`) and a `Location` header. Poll\n`links[rel=self]` on the returned entity until status is `COMPLETED` or `FAILED`.\nEach concrete resource is also reachable via `GET /operations/{operationId}`;\nthe `operationId` is included in the entity for clients that prefer the abstract\nview. Non-commercial mutations return a `DomainOperation` body.\n\n**Entity-oriented resource model.** Domains are the core entity of this API\n(exposed as `/domain-names` in the path to distinguish the resource collection\nfrom the `domains` namespace prefix). Register, renew, and transfer are\ncommercial actions executed by `POST` to their corresponding top-level\nresource collections (`/registrations`, `/renewals`, `/transfers`); each\naccepts a prior quote and returns an async entity to poll until completion.\nFor commercial execute calls, the target domain is\nexpressed in the request body, not the path. Sub-resources (`contacts`,\n`nameservers`, `privacy`, `auto-renew`, `transfer-lock`, `records`) only exist\nin the context of a specific domain-name instance. The `/check-availability`\ncontroller accepts GET (single domain) or POST (1–25 domains) and carries no\npersistent identity.\n\n**Flat, two-level maximum.** No resource path goes deeper than\n`/{collection}/{id}/{sub-resource}` or `/{collection}/{id}/{sub-collection}/{id}`.\n\n**Reseller on-behalf-of.** Resellers pass `X-Shopper-Id`; all operations are\nthen scoped to that shopper. Absent the header, the authenticated entity's own\naccount is used.\n\n## Launch Scope (v3.0)\nStandard TLDs only. TLDs with eligibility requirements (.us, .ca, .eu) return\n`UNSUPPORTED_TLD` until Phase 2.\n",
"description": "The GoDaddy Domain Lifecycle Management API provides comprehensive capabilities\nfor discovering, registering, managing, renewing, transferring, and reselling\ndomain names. This is major version 3, designed for agent-first interactions\nwhile remaining fully usable by direct API clients and resellers.\n\n## Namespace\n\nAll paths are under `/v3/domains/`. The namespace is `domains` (the business capability);\nthe core entity collection is `/domain-names`.\n\n## Key Conventions\n\n**Quote/execute for commercial operations.** Every commercial mutation (register,\nrenew, transfer) requires a `quoteToken` minted by the corresponding quote\ncollection endpoint. Execution without a prior quote is structurally impossible.\nThe token locks the price, resolved settings, and required legal agreements for a\n10-minute TTL. Quote calls are free, read-only, and safe to call speculatively.\n\n**Async commercial operations.** `POST /registrations`, `POST /renewals`, and\n`POST /transfers` each return `202 Accepted` with the concrete entity body\n(`Registration`, `Renewal`, or `Transfer`) and a `Location` header. Poll\n`links[rel=self]` on the returned entity until status is `COMPLETED` or `FAILED`.\nEach concrete resource is also reachable via `GET /operations/{operationId}`;\nthe `operationId` is included in the entity for clients that prefer the abstract\nview. Non-commercial mutations return a `DomainOperation` body, except DNS records\nand forwarding, which are applied synchronously.\n\n**Entity-oriented resource model.** Domains are the core entity of this API\n(exposed as `/domain-names` in the path to distinguish the resource collection\nfrom the `domains` namespace prefix). Register, renew, and transfer are\ncommercial actions executed by `POST` to their corresponding top-level\nresource collections (`/registrations`, `/renewals`, `/transfers`); each\naccepts a prior quote and returns an async entity to poll until completion.\nFor commercial execute calls, the target domain is\nexpressed in the request body, not the path. Sub-resources (`contacts`,\n`nameservers`, `privacy`, `auto-renew`, `transfer-lock`, `records`,\n`forwards`) only exist in the context of a specific domain-name instance.\nThe `/check-availability` controller accepts GET (single domain) or POST\n(1–25 domains) and carries no persistent identity.\n\n**Flat, two-level maximum.** No resource path goes deeper than\n`/{collection}/{id}/{sub-resource}` or `/{collection}/{id}/{sub-collection}/{id}`.\n\n**Reseller on-behalf-of.** Resellers pass `X-Shopper-Id`; all operations are\nthen scoped to that shopper. Absent the header, the authenticated entity's own\naccount is used.\n\n## Launch Scope (v3.0)\nStandard TLDs only. TLDs with eligibility requirements (.us, .ca, .eu) return\n`UNSUPPORTED_TLD` until Phase 2.\n",
"title": "Domain Lifecycle Management API",
"version": "3.5.0",
"version": "3.7.0",
"x-visibility": {
"extent": "public"
}
Expand Down
Loading
Loading